#c #return-value-optimization
Вопрос:
У меня есть неподъемный класс. Копирование этого было бы проблематичным. Я хочу гарантировать, что он никогда не будет скопирован, поэтому я создал его конструктор копий deleted
:
class A {
public:
A();
A(const Aamp;) = delete;
};
A fun() {
return A();
};
int main() {
A a = fun();
};
К сожалению, g не будет компилировать это по причине:
t.cc: In function ‘A fun()’:
t.cc:8:12: error: use of deleted function ‘A::A(const Aamp;)’
return A();
^
t.cc:4:5: note: declared here
A(const Aamp;) = delete;
^
t.cc: In function ‘int main()’:
t.cc:12:13: error: use of deleted function ‘A::A(const Aamp;)’
A a = fun();
^
t.cc:4:5: note: declared here
A(const Aamp;) = delete;
^
Но это очень четкая ситуация, в которой следует использовать функцию копирования, поэтому конструктор копирования никогда не следует вызывать. Почему это так?
Комментарии:
1. Подождите до C 17, может быть, это будет гарантировано
2. Джеспер не повторяет ваш ответ, вы не упоминали о предстоящих изменениях до наших комментариев
3. @PiotrSkotnicki Извините, я не читал ваши комментарии.
4. Решение, которое я больше всего видел в этом случае, состоит в том, чтобы переместить класс или, если он не является подвижным, вернуться
std::unique_ptr<A>
.
Ответ №1:
До тех пор, пока C 17 не выполнит оптимизацию копирования, компилятору не требуется этого делать, поэтому классы должны быть доступны для копирования, поскольку компилятор может захотеть скопировать (даже если на самом деле это не так). В C 17 копирование во многих случаях будет гарантировано, и тогда классам не понадобятся операторы копирования.
Смотрите также:
http://en.cppreference.com/w/cpp/language/copy_elision
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html
https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/ (немного о «Гарантированном копировании»)
Возможно, вы могли бы использовать старый трюк с объявлением конструктора копирования в своем классе, но на самом деле не реализовывать его? Это должно понравиться компилятору до тех пор, пока он фактически не вызовет ctor копирования. Я не проверял это, но я считаю, что это должно работать для вашего случая, пока не появится C 17.
Комментарии:
1. Да — фактически реализованный конструктор копирования выдал фатальное исключение.
Ответ №2:
Вы не можете принудительно скопировать elision (пока) (см. Другие ответы).
Однако вы можете предоставить конструктор перемещения по умолчанию для своего класса, это приведет к перемещению (и, следовательно, не к копированию) возвращаемого значения, если RVO/NRVO невозможно. Для этого вам следует добавить = default
для своих конструкторов перемещения:
class A {
public:
A() = defau<
A(const Aamp;) = delete;
A(Aamp;amp;) = defau<
Aamp; operator=(Aamp;amp;) = defau<
};
Комментарии:
1. @peterh, вероятно, потому, что он не отвечает на заданный вами вопрос.
2. @juanchopanza Это возможно, но мне непонятно, почему. Что неясно в этом ответе, так это зачем нужны конструкторы перемещения.
3. @juanchopanza Это ответ на вопрос, хотя и плохо из-за отсутствия объяснения. Это действительный способ гарантировать, что класс никогда не будет скопирован, в то же время позволяя оператору OP
return
работать.4. @peterh На практике никаких перемещений не происходит, но семантически должна быть копия или перемещение, потому что C возвращает значение по значению. В C 98 это означает, что конструктор копирования всегда необходим. В C 11 вы можете заменить его конструктором перемещения. В C 17 в некоторых случаях ни то, ни другое не понадобится
5. @juanchopanza Третий вопрос-это неявное «Как мне это сделать?» после «Я хочу гарантировать, что это никогда не будет скопировано» 🙂
Ответ №3:
Оптимизация возвращаемого значения (RVO и NRVO) не означает, что требование о том, чтобы типы, используемые для копирования или перемещения, были удалены. Это требование применяется независимо от того, получаете вы РВО или нет.
Наиболее вероятная причина этого заключается в том, что копирование (в настоящее время) не выполняется. Это оптимизация, которая может иметь место, и для кода не имеет смысла компилировать или нет, основываясь на том, применяется ли эта оптимизация в конкретной реализации.
В C 17 в некоторых обстоятельствах будет применяться RVO, и требования к копируемости и перемещаемости будут отменены.