Инициализация члена для непереписываемой переменной в С++ 17 - программирование
Подтвердить что ты не робот

Инициализация члена для непереписываемой переменной в С++ 17

При выполнении инициализации элемента для непереписываемой переменной (такой как std::atomic<int>), он должен использовать direct-initialization вместо copy-initialization в соответствии с ответом здесь. Однако, когда я включаю -std=c++17 в g++ 7.4.0, кажется, что последний тоже хорошо работает.

#include <atomic>

class A {
    std::atomic<int> a = 0;     // copy-initialization
    std::atomic<int> b{0};      // direct-initialization
};
$ g++ -c atomic.cc -std=c++11    // or c++14
atomic.cc:4:26: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)
     std::atomic<int> a = 0;     // copy-initialization

$ g++ -c atomic.cc -std=c++17
// no error

Это также не удалось при компиляции с g++ 6.5.0 даже с -std=c++17. Какой из них здесь правильный?

4b9b3361

Ответ 1

Поведение изменилось с C++ 17, который требует от компиляторов пропустить конструкцию копирования/перемещения в std::atomic<int> a = 0;, т.е. гарантированное разрешение копирования.

(выделение мое)

При следующих обстоятельствах компиляторы должны опустить конструкцию объектов копирования и перемещения, даже если конструктор копирования/перемещения и деструктор имеют наблюдаемые побочные эффекты. Объекты создаются непосредственно в хранилище, где они в противном случае были бы скопированы/перемещены. Конструкторы копирования/перемещения не обязательно должны присутствовать или быть доступными, так как языковые правила гарантируют, что операция копирования/перемещения не выполняется, даже концептуально:

Подробно, std::atomic<int> a = 0; выполняет инициализацию копирования:

Если T является типом класса и cv-неквалифицированная версия типа other не является T или не получена из T, или если T не является типом класса, но тип other является типом класса, определяемые пользователем последовательности преобразования которые могут преобразовывать из типа другого в T (или в тип, производный от T, если T является типом класса и доступна функция преобразования), и выбирается лучший из них с помощью разрешения перегрузки. Результат преобразования, который представляет собой prvalue temporary (until C++17) prvalue expression (since C++17), если использовался конструктор преобразования, затем используется для прямой инициализации объекта.

и

(выделение мое)

если T является типом класса и инициализатор является выражением prvalue, чей cv-unqualified тип является тем же классом, что и T, само выражение инициализатора, а не временное материализованное из него, используется для инициализации целевого объекта

Это означает, что a инициализируется непосредственно из 0, временное построение не требуется, а затем больше не требуется временное копирование/перемещение.

До C++ 17 в концепции std::atomic<int> a = 0; требуется, чтобы временный std::atomic создавался из 0, тогда временный используется для копирования-построения a.

Даже разрешение на копирование разрешено до C++ 17, это считается оптимизацией:

(выделение мое)

Это оптимизация: даже когда это происходит и конструктор copy/move (since C++11) не вызывается, он все равно должен присутствовать и быть доступным (как если бы оптимизация вообще не происходила), в противном случае программа плохо сформирована:

Вот почему gcc запускает диагностику в режиме до C++ 17 для std::atomic<int> a = 0;.

(выделение мое)

Примечание: вышеприведенное правило не определяет оптимизацию: спецификация C++ 17 основного языка значений и временных значений принципиально отличается от таковой в более ранних ревизиях C++: больше нет временного элемента для копирования/перемещения из. Еще один способ описать механику C++ 17 - это "нематериализованная передача значений": значения возвращаются и используются без материализации временного.

BTW: I suppose there was a bug in [TG412] with [TG413]; and it has been fixed in later version.

Ответ 2

  Какой из них правильный здесь?

7.4.0 правильно. Копия может быть исключена для этого случая, поэтому она в порядке. (хотя для этого требуется ).

(см. https://en.cppreference.com/w/cpp/language/copy_initialization для более подробной информации)