пятница, 7 ноября 2014 г.

Специальные функции-члены в C++

Здесь есть хорошее описание специальных функции-членов С++ от Howard Hinnant (один из основных комиттеров в Clang STL). Специальные функции-члены - это те, которые компилятор генерирует, если они не объявлены явно:

  • Конструктор по умолчанию (Default constructor);
  • Деструктор (Destructor);
  • Конструктор копирования (Copy constructor);
  • Оператор копирующего присваивания (Copy assignment);
  • Конструктор переноса (Move constructor);
  • Оператор переносящего присваивания (Move assignment);
Или в коде:
struct MyStruct {
    MyStruct() = default;
    ~MyStruct() = default;
    MyStruct(const MyStruct &) = default;
    MyStruct &operator=(const MyStruct &) = default;
    MyStruct(MyStruct &&) = default;
    MyStruct &operator=(MyStruct &&) = default;
};
Каждая из этих функций может быть:
  • Не объявлена;
  • Может быть сгенерирована компилятором:
    • Как default - вызывает соответствующие функции для членов. Для конструктора - конструкторы членов, для оператора присваивания - операторы присваивания членов и т.д.
    • Как delete - если соответствующая функция у члена объявлена как delete или функция не может быть сгенерирована. Например для оператора присваивания член может иметь оператор присваивания объявленный как delete, или объект не может копироваться, т.к. содержит ссылку.
  • Может быть объвлена явно программистом:
    • Как default;
    • Как delete;
    • С телом функции;
Разница между "не объявлена" и объявлена с delete в том, что не объявленная функция не участвует в разрешении перегрузки.

Например класс:
struct MyStruct {
    template <typename... Args>
    MyStruct(Args...) {
    }
};
может быть создан вызовом конструктора без параметров, т.к. конструктор по умолчанию не объявлен, и не участвует в разрешении перегрузки.
Но такой класс:

struct MyStruct {
    template <typename... Args>
    MyStruct(Args...) {
    }
    MyStruct() = delete;
};
создать вызовом конструктора без параметров не получится, т.к. конструктор по умолчанию объявлен и участвует в разрешении перегрузки, и при этом он объявлен как delete.
При этом стоить помнить одно правило - если перемещающие функции объявленны компиляторм как delete, они не участвуют в разрешении перегрузки, не смотря на факт объвления.
Когда и какие функции компилятор генерирует сам, а когда объявляет их как delete?
Объявленный конструктор по умолчанию - остальные 5 функций объявляются компилятором как default:
struct MyStruct {
    MyStruct() = default;
    ~MyStruct() = default;
    MyStruct(const MyStruct &) = default;
    MyStruct &operator=(const MyStruct &) = default;
    MyStruct(MyStruct &&) = default;
    MyStruct &operator=(MyStruct &&) = default;
};
Объявленный деструктор - конструктор перемещения и перемещающий оператор присваивания становятся не объявленными, остальные функции объявляются компилятором как default:
struct MyStruct {
    MyStruct() = default;
    ~MyStruct() = default;
    MyStruct(const MyStruct &) = default;
    MyStruct &operator=(const MyStruct &) = default;


};
Объявленный конструктор копирования - конструктор по умолчанию, конструктор перемещения и перемещающий оператор присваивания становятся не объявленными, остальные функции объявляются компилятором как default:
struct MyStruct {

    ~MyStruct() = default;
    MyStruct(const MyStruct &) = default;
    MyStruct &operator=(const MyStruct &) = default;


};
Объявленный копирующий оператор присваивания - конструктор перемещения и перемещающий оператор присваивания становятся не объявленными, остальные функции объявляются компилятором как default:
struct MyStruct {
    MyStruct() = default;
    ~MyStruct() = default;
    MyStruct(const MyStruct &) = default;
    MyStruct &operator=(const MyStruct &) = default;


};
Объявленный конструктор перемещения - конструктор по умолчанию и перемещающий оператор присваивания становятся не объявленным, деструктор объявляется компилятором как default, остальные функции объявляются компилятором как delete.
struct MyStruct {

    ~MyStruct() = default;
    MyStruct(const MyStruct &) = delete;
    MyStruct &operator=(const MyStruct &) = delete;
    MyStruct(MyStruct &&) = default;

};
Объявленный перемещающий оператор присваивания - конструктор по умолчанию и деструктор объявляется компилятором как default, конструктор копирования и оператор копирующего присваивания объявляются компилятором как delete, конструктор перемещения становится не объявленным.
struct MyStruct {
    MyStruct() = default;
    ~MyStruct() = default;
    MyStruct(const MyStruct &) = delete;
    MyStruct &operator=(const MyStruct &) = delete;

    MyStruct &operator=(MyStruct &&) = default;
};

Комментариев нет:

Отправить комментарий