Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Вызывается ли конструктор перемещения после вызова функции преобразования?

Автор вопроса: BarryОтветов: 1

Рассмотрим этот пример:

struct T { };

struct S {
    operator T();
};

S s;
T t = s;

[dcl.init] приведет нас к [over.match.copy], который найдет функцию преобразования operator T(). Но на этом мы закончили или нам нужно вызвать T(T&& rhs), привязав rhs к возврату operator T() через [dcl.init.ref]? Есть ли различия в ответе на этот вопрос между С++ 11 и С++ 1z?

  • Я считаю, что для инициализации копирования требуется наличие доступного конструктора копирования (для T), но он будет использовать конструктор перемещения, если он доступен (или полностью исключить копию, в зависимости от реализации operator T() он может применить NRVO). cdhowie 01.06.2017

c++initializationc++17c++11language-lawyer

Ответы

Ответ 1 · рейтинг 15 · T.C.

Это подпадает под [dcl.init]/17.6.3. , что довольно ясно показывает, что происходит после того, как разрешение перегрузки выбирает функцию преобразования:

Выбранная функция вызывается с выражением инициализатора в качестве аргумента; если функция является конструктором, вызов является значением prvalue неквалифицированной cv версии целевого типа, объект результата которого инициализируется конструктором. Вызов используется для прямой инициализации в соответствии с приведенными выше правилами объекта, который является местом назначения инициализации копирования.

В вашем случае это, в свою очередь, превращается в [dcl.init]/17.6. 1а>:

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


В C++11 второй шаг вызывает конструктор перемещения, так как он не имеет маркера, соответствующего 17.6.1 C++17. Вместо этого вы снова танцуете прямую инициализацию/разрешение перегрузки а>:

Если инициализация является прямой инициализацией, [...] рассматриваются конструкторы. Перечисляются применимые конструкторы ([over.match.ctor]), а лучший из них выбирается с помощью разрешения перегрузки ([over.match]). Выбранный таким образом конструктор вызывается для инициализации объекта с выражением инициализатора или списком выражений в качестве аргумента(ов). Если конструктор не применяется или разрешение перегрузки неоднозначно, инициализация имеет неправильный формат.

Этот ход можно (и на практике) исключить; см. [class.copy]/31.


На самом деле более интересный случай

T t(s);

который в соответствии с формулировкой C++17 фактически требуется для вызова конструктора перемещения, поскольку он использует правило прямой инициализации и разрешает перегрузку конструкторов T. Это выбирает конструктор перемещения T и вызывает его для инициализации t, преобразуя s в значение T prvalue, которое материализуется во временное и привязывается к параметру конструктора перемещения. Маркер 17.6.1 просто недоступен в процессе, а маркер в C++11 [class.copy]/31 (теперь [class.copy.elision]/1), допускавший исключение в этом сценарии, был удален в C++17.

Скорее всего это дефект.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы