Можем ли мы вернуть объекты, имеющие удаленный / закрытый экземпляр / механизм перемещения по значению из функции?

В C ++ 03 невозможно вернуть объект classа, имеющий частный не определенный конструктор копирования, по значению:

struct A { A(int x) { ... } private: A(A const&); }; A f() { return A(10); // error! return 10; // error too! } 

Мне было интересно, было ли это ограничение отменено на C ++ 11, позволяя писать функции, имеющие тип возвращаемого типа classа для classов без конструкторов, используемых для копирования или перемещения? Я помню, что было бы полезно разрешить вызывающим функциям использовать вновь возвращенный объект, но они не могут скопировать значение и сохранить его где-нибудь.

Вот как он может работать

 A f() { return { 10 }; } 

Это работает, хотя у A нет рабочей копии или перемещения конструктора и нет другого конструктора, который мог бы скопировать или переместить A !

Чтобы использовать эту функцию C ++ 11, конструктор (взяв int в этом случае) должен быть неявным, хотя.

Ограничение не отменено. В соответствии с спецификатором доступа в §12.8 / 32 есть примечание, которое объясняет:

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

Что касается удаленных копий / перемещений конструкторов §8.4.3 / 2, то указывается, что

Программа, которая ссылается на удаленную функцию неявно или явно, кроме объявления, плохо сформирована. [Примечание. Это включает вызов функции неявно или явно и формирование указателя или указателя на элемент функции. Он применяется даже для ссылок в выражениях, которые потенциально не оцениваются. Если функция перегружена, она ссылается только в том случае, если функция выбрана с помощью разрешения перегрузки. – конечная нота]

Не уверен в этом конкретном случае, но мое понимание цитаты состоит в том, что если после разрешения перегрузки в §12.8 / 32 выбран удаленный экземпляр / механизм перемещения, даже если операция отменена, это может быть ссылкой на функцию , и программа будет плохо сформирована.

Вышеприведенный код по-прежнему плохо сформирован в C ++ 11. Но вы можете добавить конструктор public move в A а затем это будет законным:

 struct A { A(int x) {} A(A&&); private: A(A const&); }; A f() { return A(10); // Ok! } 

Мне было интересно, это ограничение было отменено на C ++ 11?

Как это могло произойти? Возвращая что-то по значению, вы по определению копируете (или перемещаете) его. И хотя C ++ позволяет разрешить копирование / перемещение в определенных обстоятельствах, он все еще копирует (или перемещает) спецификацию.

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

Да. Вы избавляетесь от конструктора / назначения копии, но допускаете перемещение значения . std::unique_ptr делает это.

Вы можете вернуть значение unique_ptr . Но при этом вы возвращаете «prvalue»: временное, которое уничтожается. Поэтому, если у вас есть функция g как таковая:

 std::unique_ptr g() {...} 

Вы можете сделать это:

 std::unique_ptr value = g(); 

Но не это :

 std::unique_ptr value1 = g(); std::unique_ptr value2 = g(); value1 = value 2; 

Но это возможно:

 std::unique_ptr value = g(); value = g(); 

Вторая строка вызывает оператор присваивания перемещения по value . Он удалит старый указатель и переместит в него новый указатель, оставив старое значение пустым.

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

Удаление конструкторов копирования и перемещения создает неподвижный объект. Там, где он создан, это то, где его ценности остаются навсегда . Движение позволяет вам иметь уникальную собственность, но не быть неподвижным.

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

Что-то вроде:

 template struct ReturnProxy { //This could be made private, provided appropriate frienship is granted ReturnProxy(T* p_) : p(p_) { } ReturnProxy(ReturnProxy&&) = default; private: //don't want these Proxies sticking around... ReturnProxy(const ReturnProxy&) = delete; void operator =(const ReturnProxy&) = delete; void operator =(ReturnProxy&&) = delete; struct SUPER_FRIENDS { typedef T GO; }; friend struct SUPER_FRIENDS::GO; unique_ptr p; }; struct Object { Object() : data(0) { } //Pseudo-copy constructor Object(ReturnProxy&& proxy) : data(proxy.p ? proxy.p->data : throw "Don't get sneaky with me \\glare") { //steals `proxy.p` so that there isn't a second copy of this object floating around //shouldn't be necessary, but some men just want to watch the world burn. unique_ptr thief(std::move(proxy.p)); } private: int data; Object(const Object&) = delete; void operator =(const Object&) = delete; }; ReturnProxy func() { return ReturnProxy(new Object); } int main() { Object o(func()); } 

Возможно, вы могли бы сделать то же самое в 03, хотя, используя auto_ptr s. И это, очевидно, не мешает хранению результирующего Object , хотя он ограничивает вас одной копией на экземпляр.

  • механизм вызова конструктора
  • Почему конструктор копирования не вызывается?
  • Копировать конструктор и = перегрузка оператора в C ++: возможна ли общая функция?
  • Clone () vs Copy constructor - который рекомендуется в java
  • Почему этот код пытается вызвать конструктор копирования?
  • Оператор копирования и оператор присваивания
  • Почему конструктор копирования принимает свой параметр по ссылке в C ++?
  • Давайте будем гением компьютера.