#c #c 17 #c 14 #language-lawyer #copy-constructor
Вопрос:
Я хотел бы выяснить, почему приведенный ниже код не компилируется с C 14, но прекрасно компилируется с C 17. Есть идеи, что можно было бы изменить с C 17? Дело, конечно, в неконстантном конструкторе копирования класса A. Я использую VS 2019. Действителен ли вообще этот код?
class A {
public:
A() { }
A(Aamp; a) { }
};
A fun() {
A a;
return a;
}
int main()
{
A a = fun();
}
Сообщения от компилятора:
- класс A не имеет подходящего конструктора копирования
- инициализация не может преобразовать из A в A
- Невозможно скопировать конструкцию класса A из-за неоднозначных конструкторов копирования или отсутствия доступного конструктора копирования
Комментарии:
1. Одна из проблем заключается в том, что MSVC с давних пор имеет расширение, которое позволяет привязывать временные файлы непостоянными ссылками.
2. Вероятно, это связано с обязательным копированием elision начиная с C 17
3. @dewaffled Я так не думаю, так как это случай N RVO, когда копирование не является обязательным.
4. пожалуйста, включите сообщение об ошибке в вопрос
5. @Jodocus copy-elision-это не только функция в операторах возврата, правила, касающиеся ее при инициализации объекта, также были обновлены в c 17. Это та часть, которая здесь уместна.
Ответ №1:
fun()
является значением типа A
prvalue , поэтому A a = fun();
означает, что a
это объект результата вызова функции, промежуточного временного значения нет.
Текст для этого находится в C 17 [basic.lval]/2:
Результирующим объектом значения prvalue является объект, инициализированный значением prvalue;
Это было бы то же самое для A a = A(A(A(A(A(fun())))));
etc. — все значения prvalues имеют a
в качестве результирующего объекта.
Поведение оператора return приведено в [stmt.return]/2:
return
оператор инициализирует объект результата glvalue или результата prvalue вызова (явной или неявной) функции с помощью инициализации копирования (11.6) из операнда.
Результирующий объект может быть успешно инициализирован путем инициализации копирования из a
(локальной переменной fun
), поскольку это значение не является постоянным, и поэтому конструктор копирования, принимающий ссылку на значение не является постоянным, привязывается к нему.
До C 17 fun()
возвращаемое значение было временным объектом, а затем объект main a
был скопирован/перемещен из временного, при этом elision был необязательным (но для существования действительного конструктора требовалось существование).
Комментарии:
1.
return
оператор инициализирует результат glvalue … (явного или неявного) вызова функции Вместе с [expr.вызов]/9 говоря, что результат вызова функции является результатом возможно преобразованного операндаreturn
оператора , это означает, чтоreturn
оператор инициализирует свой операнд (его результат) из своего операнда. Вот почему мы любим C .2. @LanguageLawyer Да, я думаю, что предложение из [stmt.return]/2 немного ошибочное, но намерение кажется ясным