Требуется ли конструктор копирования при возврате неявным преобразованием?

Следующий код прекрасно компилируется в Visual C ++ 2013, но не в GCC или Clang.

Что правильно?
Требуется ли доступный конструктор копирования при возврате объекта через неявное преобразование?

class Noncopyable
{
Noncopyable(Noncopyable const &);
public:
Noncopyable(int = 0) { }
};

Noncopyable foo() { return 0; }

int main()
{
foo();
return 0;
}

GCC:

error: 'Noncopyable::Noncopyable(const Noncopyable&)' is private
Noncopyable(Noncopyable const &);
^
error: within this context
Noncopyable foo() { return 0; }

Clang:

error: calling a private constructor of class 'Noncopyable'
Noncopyable foo() { return 0; }
^
note: implicitly declared private here
Noncopyable(Noncopyable const &);
^
warning: C++98 requires an accessible copy constructor for class 'Noncopyable' when binding a reference to a temporary; was private [-Wbind-to-temporary-copy]
Noncopyable foo() { return 0; }
^
note: implicitly declared private here
Noncopyable(Noncopyable const &);
^

22

Решение

Когда ты return выражение, временный объект возвращаемого типа создается, инициализируется с этим выражением, а затем перемещается (или копируется, если перемещение не является опцией), в возвращаемое значение. Так что вам нужен доступный конструктор копирования или перемещения.

Однако возможно инициализировать возвращаемое значение напрямую, используя список в скобках. Итак, следующие работы:

Noncopyable foo() { return {0}; }

Подобный случай в живой пример.

15

Другие решения

12.8 Копирование и перемещение объектов класса [class.copy]

1 / Объект класса может быть скопирован или перемещается двумя способами: инициализацией (12.1, 8.5), в том числе для аргумента функции
прохождение (5.2.2) и для возврата значения функции (6.6.3); […]

В 6.6.3 Оператор возврата [stmt.return]:

2 / […] Значение выражения неявно
преобразован в тип возврата функции, в которой он появляется. Оператор возврата может включать
строительство и копирование
или двигаться временного объекта (12.2) […]

а также 12.2 Временные объекты [класс.время]:

1 / Созданы временные классы в различных контекстах: привязка ссылки к prvalue (8.5.3), возврате
prvalue (6.6.3), преобразование, которое создает prvalue (4.1, 5.2.9, 5.2.11, 5.4)
, […] Примечание: даже если нет вызова деструктора или конструктора копирования / перемещения, все
семантические ограничения, такие как доступность (пункт 11) и удаление функции (8.4.3), должны
быть довольным. […]

Я бы сказал, что GCC и clang верны — я бы даже сказал, что каждый раз, когда вы возвращаете значение, у возвращаемого типа должен быть доступный конструктор копирования или перемещения.

Логика заключается в том, что временный создается для преобразования исходного типа в новый тип (int в Noncopyable), а затем создается копия этого временного объекта для возврата в функцию.

Это так же, как:

Noncopyable foo() { return Noncopyable(0); }

Вы ожидаете, что копия будет требоваться там? Я бы конечно

5

  • функция foo возвращает Noncopyable объект по значению. Таким образом, теоретически должен быть вызван конструктор копирования.

  • Если вы сделаете конструктор копирования доступным (т.е. public) и распечатайте сообщение, чтобы пометить его вызов, вы увидите, что это сообщение не распечатано DEMO и вызывается только перегруженный оператор преобразования.

  • Это связано с фактом оптимизации копирования.

  • Таким образом, дело не в том, что перегруженному оператору преобразования требуется конструктор копирования, а в выражении возврата foo требуется конструктор копирования, потому что вы возвращаете по значению.

  • В конце концов, конструктор копирования не будет вызван из-за удаления копии, но все же должен быть доступен.

4
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector