Как принудительно использовать функцию копирования, почему она не будет работать с конструктором удаленных копий?

#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, и требования к копируемости и перемещаемости будут отменены.