Оптимизация возвращаемого значения в Visual Studio 2015?

В запросе я использовал код, похожий на следующий:

class C {
public:
    C() {}

    C(const C&) = delete;
};

C f() {
    return C();
}

int main() {
    f();
}

В каждом предыдущем компиляторе Visual С++ я использовал (до 2013 года), что никогда не было проблемой. Но когда я пытаюсь скомпилировать его с новым компилятором Visual С++ 2015, я получаю следующую ошибку:

1>c:\devel\c++11\playground\main.cpp(10): error C2280: 'C::C(const C &)': attempting to reference a deleted function
1>  c:\devel\c++11\playground\main.cpp(6): note: see declaration of 'C::C'

Я не уверен, почему он работал ранее, но я предполагаю, что из-за оптимизации значения возвращаемого значения был вызван конструктор по умолчанию, а не конструктор копирования.

Является ли код, который я использовал даже законным С++? А если нет, то каков был бы правильный способ реализации этого кода, не требуя конструктора копирования для моего класса C? Я мог бы, конечно, использовать конструктор перемещения, но потом я предполагаю, что код никогда не был бы допустимым С++ до С++ 11?

Ответ 1

Аргументы функции и возвращаемые значения инициализируются с помощью copy-initialization. Для инициализации копирования требуется, чтобы конструкторы копирования были доступны, даже если они были отменены (N) RVO:

Если T - тип класса, а тип другого - другой, или T - тип неклассов, но тип другого - это тип класса, определяемые пользователем последовательности преобразования, которые могут преобразовываться из типа другого к T (или к типу, полученному из T, если T является типом класса и доступна функция преобразования), и наилучший выбирается с помощью разрешения перегрузки. Результат преобразования, который является временным значением prvalue, если использовался конструктор преобразования, затем используется для прямого инициализации объекта. Последний шаг обычно оптимизируется, и результат преобразования создается непосредственно в памяти, выделенной для целевого объекта, но соответствующий конструктор (перемещение или копирование) должен быть доступен, даже если он не используется.

Ответ 2

Вам нужно следовать правилу 5 из 5. Поскольку вы удалили конструктор копирования, вам необходимо определить конструктор перемещения.

C(C&&) = default;
C& operator=(C&&) = default;

С конструктором перемещения - работает
Без перемещения конструктора - не работает, нарушает правило 5

Обратите внимание, что указанный сайт использует gcc и даже не компилируется, поэтому он не является специфичным для Visual Studio, это определенное и ожидаемое поведение.

Ответ 3

Возможно, ранее ему удалось выбрать неявно созданный конструктор перемещения для этого, но в VS 2015 неявное генерирование конструктора перемещения блокируется наличием операций (-ов) копирования, которое, если я правильно напомню, является стандартным поведением.

Поэтому вам просто нужно определить конструктор перемещения самостоятельно, возможно, a = default.