• Как в деструкторе базового класса вызвать переопределённый метод?

    @code_panik
    Порядок вызова деструкторов определяется стандартом языка. Сначала выполняется деструктор дочернего класса, затем деструкторы членов дочернего класса и только потом родительские деструкторы (https://isocpp.org/wiki/faq/dtors#order-dtors-for-...). Поэтому доступные данные и методы в родительском деструкторе ограничены родительским подобъектом и его родителями.
    Родительский деструктор можно сделать тривиальным ~Parent() = default (ничего не делать) или чистым виртуальным virtual ~Parent() = 0 (не определен). Только во втором случае невозможно создавать объекты типа "Parent".
    Ответ написан
    Комментировать
  • Почему msvc оптимизирует конструкторы несмотря на флаги?

    @code_panik
    Разберемся, в каком порядке выводятся сообщения на экран во втором случае gnu++11.
    Сначала в main вызываются два конструктора по умолчанию: для imnotdumb1 и временного объекта dumb_array();.
    Далее в main вызывается метод dumb_array & operator=( dumb_array temp), в котором temp инициализируется посредством метода dumb_array (dumb_array&& other), который перед выводом на экран сообщения operator=( dumb_array tmp) вызывает ещё один конструктор по умолчанию.
    Итого 3 конструктора по умолчанию, один перемещения, один оператор присваивания.

    MSVC вызывает первые два конструктора по умолчанию и применяет move elision к инициализации temp.
    То есть весь пример сводится к такому коду
    #include <iostream>
    using namespace std;
    
    struct Foo {
    	Foo() { cout << "default ctor\n"; }
    	Foo(Foo&& arg) { cout << "move ctor\n"; }
    };
    
    void foo(Foo arg) {
    	cout << "foo\n";
    }
    
    int main () {    
    	foo(Foo());
    	return 0;
    }


    В c++ существуют обязательные (mandatory) и необязательные случаи copy/move elision, https://en.cppreference.com/w/cpp/language/copy_elision.
    Применение правила из примера
    In the initialization of an object, when the source object is a nameless temporary and is of the same class type (ignoring cv-qualification) as the target object.

    до c++17 было необязательным к реализации, на усмотрение разработчиков компилятора.
    В Microsoft считают такое правило обязательным вне зависимости от флагов компиляции,
    Mandatory copy/move elision in Visual Studio

    The C++ standard requires copy or move elision when the returned value is initialized as part of the return statement (such as when a function with return type Foo returns return Foo()). The Microsoft Visual C++ compiler always performs copy and move elision for return statements where it is required to do so, regardless of the flags passed to the compiler. This behavior is unchanged.


    До c++17 требовалось, чтобы соответствующий copy/move ctor в случае copy elision всё равно был доступен (cppreference, там же):
    This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed
    Ответ написан
    1 комментарий
  • Почему нет доступа к приватному атрибуту?

    @code_panik
    Пожалуйста, приводите в вопросах содержимое файлов с кодом полностью. Без полных листингов нельзя точно сказать, в чем проблема.
    Обратите внимание, что для вызова operator+(Car& c1, Car& c2) функция GetCar должна возвращать Car&, а не Car или const Car& (только не возвращайте ссылку на переменную, объявленную внутри функции).
    Замените везде operator+(Car& c1, Car& c2) на operator+(const Car& c1, const Car& c2).
    Работающий пример.
    И замените, по возможности, компилятор на более современный.
    Ответ написан
  • Как убрать ошибки взаимодействия между классами?

    @code_panik
    Если структуру классов не нужно менять, то можно сделать как в рабочем примере.
    Перед Window_mgr достаточно объявить (forward declare) class Screen. Просто нужно иметь ввиду, что в файле класса Window_mgr это неполный тип (incomplete type).
    И во friend объявлении не хватает Window_mgr::ScreenIndex.
    Ответ написан
    1 комментарий
  • Как это можно реализовать?

    @code_panik
    Если enum не видно извне как деталь реализации, то можно так.
    #include <iostream>
    using namespace std;
    
    class BaseTransport {    
    public:
        virtual void Move() {        
            onArrival();            
        }
    private:
        virtual void onArrival() = 0;
    };
    
    class GroundTransport : public BaseTransport {
    private:
        void onArrival() override {
            cout << __PRETTY_FUNCTION__ << endl;
            state = GroundEnum::Finished;
        }
        enum class GroundEnum { Finished };    
        GroundEnum state;
    };
    
    class SeaTransport : public BaseTransport {    
    private:
        void onArrival() override {
            cout << __PRETTY_FUNCTION__ << endl;
            state = SeaEnum::ReturnToBegin;
        }
        enum class SeaEnum { ReturnToBegin };
        SeaEnum state;
    };
    
    int main() {
        BaseTransport* st = new SeaTransport();
        st->Move();    
        BaseTransport* gt = new GroundTransport();
        gt->Move();
        return 0;
    }
    Ответ написан
    Комментировать
  • Почему при определении шаблона функции, принадлежащего шаблону класса, не нужно указывать шаблонный тип?

    @code_panik
    // non-type partial specialization 'func' is not allowed

    Буквально значит, что язык позволяет определить частичную специализацию только для пользовательского типа. Частичная специализация функции запрещена.
    Общий вид частичной специализации можно посмотреть здесь. Видно, что в определениях есть непустые угловые скобки после template и после имени, как и в примере из вопроса.
    Значит, скобки после имени функции в определении не нужны, если это не полная специализация. И в полной специализации скобки опускают, если тип выводится из типов аргументов (пример).
    Ответ написан