Конструктор неконстантного копирования отлично компилируется с C 17

#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();
}
 

Сообщения от компилятора:

  1. класс A не имеет подходящего конструктора копирования
  2. инициализация не может преобразовать из A в A
  3. Невозможно скопировать конструкцию класса 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 немного ошибочное, но намерение кажется ясным