Подтвердить что ты не робот

Можно ли вернуть локальную переменную по значению в С++ 11/14, чтобы возвращаемое значение было построено по rvalue, когда не задействовано копирование/перемещение?

Я знаю, что в следующей ситуации компилятор может свободно перемещать-построить возвращаемое значение из makeA (но также свободно удалять копию или вообще перемещаться):

struct A
{
    A(A&);
    A(A&&);
};

A makeA()
{
    A localA;
    return localA;
}

Интересно, разрешено ли компилятору создавать объект типа A из локального объекта типа B по ссылке rvalue, если он строится в операторе return. Другими словами, в следующем примере компилятору разрешено выбирать конструктор A для возвращаемого значения?

struct B { };
struct A {
    A(A&);  // (1)
    A(A&&); // (2)
    A(B&);  // (3)
    A(B&&); // (4)
};

A makeA()
{
    B localB;
    return localB;
}

Я прошу об этом, потому что мне кажется, что та же логика, которая позволяет локальному объекту типа A обрабатываться как rvalue в операторе return, также должна позволять локальному из любого типа обрабатываться как rvalue, но я не могу найти примеров или вопросов такого рода.

4b9b3361

Ответ 1

Правило для этой ситуации изменилось между 2011 и 2014 годами. Теперь компилятор должен рассматривать localB как rvalue.

Применимое правило для операторов return содержится в §12.8 [class.copy]/p32, которое читается на С++ 14 (цитирование N3936, основное внимание):

Когда критерии для выполнения операции копирования/перемещения выполняются, но не для объявления исключения, и объект, который нужно скопировать, обозначается lvalue или , когда выражение в возврате statement является (возможно, в скобках) id-выражением, которое называет объект с автоматическим временем хранения, объявленным в теле или Параметр-объявление-предложение самой внутренней закрывающей функции или лямбда-выражения, разрешение перегрузки для выбора конструктора для копии сначала выполняется так, как если бы объект был назначен Rvalue. Если первое разрешение перегрузки выходит из строя или не выполняется, или если тип первого параметра выбранного конструктора не ссылка rvalue на тип объектов (возможно, cv-квалифицированный), разрешение перегрузки выполняется снова, рассматривая объект как именующий.

Предложение bolded было добавлено CWG issue 1579, что явно требует вызова конструктора перемещения преобразования A::A(B&&). Это реализовано в GCC 5 и Clang 3.9.

Еще в 2011 году это правило "try rvalue first" было тесно связано с критериями для копирования (цитирование N3337):

Когда критерии для исключения операции копирования выполняются или будут сэкономленные за тот факт, что исходный объект является параметром функции, и подлежащий копированию объект обозначается значением lvalue, перегрузкой разрешение для выбора конструктора для копии сначала выполняется как будто объект был обозначен rvalue.

Поскольку для копирования экземпляра требуется, чтобы оба имели один и тот же тип, этот абзац не применялся, и компилятору пришлось использовать конструктор A::A(B&).

Обратите внимание, что поскольку CWG 1579 считается DR против С++ 11, компиляторы должны реализовать свое разрешение даже в режиме С++ 11.