Должна ли инициализация присваивания работать с типом с неявным единственным параметром, но с удаленным перемещением?

Следует ли компилировать следующее по правилам С++ 11? Почему должно или почему нет? УБ есть? Кажется, что gcc запрещал это раньше, но передумал в версии 11. Microsoft принимает это, а clang постоянно нет.

Я был под выражением, что IntWrapper myInt = 42; в этом случае просто синтаксический сахар и точно такой же, как IntWrapper myInt(42);, и что перегрузка оператора присваивания не влияет на инициализацию. Мне кажется, что старые версии gcc и все версии clang хотят сделать ctor(int), а затем move. В то время как msvc и новая версия gcc просто вызывают ctor(int), что я считаю правильным. Зачем делать ход, когда он не нужен.

Если бы какой-нибудь юрист языка С++ мог перевести это на английский, см. 11.10.1 Явная инициализация, страница 294-295 здесь https://isocpp.org/files/papers/N4860.pdf

Alternatively, a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply;

Note: Overloading of the assignment operator (12.6.2.1) has no effect on initialization. —

Как я понял, стандарт заключается в том, что компилятор может выбрать либо копирование, а затем перемещение, либо прямую инициализацию с использованием ctor, принимающего один аргумент. Что было бы странно, потому что как бы вы тогда узнали, компилируется он или нет.

#include <iostream>

struct IntWrapper
{
  IntWrapper(int value) : m_value(value)
  {
      std::cout << "ctor(int)\n";
  }

  IntWrapper(IntWrapper&& that) = delete;
  int m_value;
};

int main()
{
    IntWrapper myInt = 42;
    return 0;
}
компиляторрезультат
msvc v.19.xкомпилирует
gcc 11.xкомпилирует
gcc 10.xошибка: использование удаленной функции 'IntWrapper::IntWrapper(IntWrapper&&)
лязг 14.0.0ошибка: копирование переменной типа IntWrapper вызывает удаленный конструктор
лязг 13.0.0ошибка: копирование переменной типа IntWrapper вызывает удаленный конструктор
лязг 12.0.0ошибка: копирование переменной типа IntWrapper вызывает удаленный конструктор
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
15
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

msvc v.19.x и gcc 11.x по умолчанию используют C++17 в качестве языкового стандарта для компиляции, в то время как все другие компиляторы, которые вы использовали, по умолчанию используют C++14. Вот почему вы видите разницу.

До C++17 IntWrapper myInt = 42; семантически обрабатывается как IntWrapper myInt = IntWrapper(42);, поэтому вам нужна неудаленная копия или конструктор перемещения, чтобы скомпилировать это, даже если временный объект исключен из-за оптимизации компилятора.

Поскольку C++17 IntWrapper myInt = 42; теперь считается выполненным IntWrapper myInt{42}; и теперь мы напрямую конструируем, временный объект не создается. Эта функция называется гарантированное удаление копии.


Когда стандарт говорит

Either direct-initialization semantics or copy-initialization semantics apply;

Они означают, что IntWrapper myInt = 42; можно рассматривать как IntWrapper myInt = IntWrapper(42); или IntWrapper myInt{42};. Это зависит от того, включена ли у вас оптимизация или нет, от того, какую форму вы получаете. Дополнительную часть стандарта, из-за которой это не работает до C++17, можно найти в [класс.временный]/1.

Even when the creation of the temporary object is unevaluated (Clause [expr]) or otherwise avoided ([class.copy]), all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed. [ Note: This includes accessibility ([class.access]) and whether it is deleted, for the constructor selected and for the destructor. However, in the special case of a function call used as the operand of a decltype-specifier ([expr.call]), no temporary is introduced, so the foregoing does not apply to the prvalue of any such function call. — end note ]

Другие вопросы по теме