С++ 11 "Недвижущийся" тип

Возможный дубликат:
Почему С++ 11-удаленные функции участвуют в разрешении перегрузки?

У меня есть два вопроса о следующем коде С++ 11:

#include <iostream>

using namespace std;

struct A {
  A()  { cout << "Default c-tor" << endl; }
  A(const A&)  { cout << "Copy c-tor" << endl; }
  A(A&&) = delete;
};

A f()
{
 A a;
 return a;
}

int main()
{
  A b = f();
  return 0;
}

Я получаю следующие ошибки компиляции с помощью gcc и clang

gcc-4.7.2 (g++ --std = С++ 11 main.cpp):

main.cpp: In function ‘A f()’:
main.cpp:16:9: error: use of deleted function ‘A::A(A&&)’
main.cpp:8:2: error: declared here
main.cpp: In function ‘int main()’:
main.cpp:21:10: error: use of deleted function ‘A::A(A&&)’
main.cpp:8:2: error: declared here

clang-3.0 (clang++ --std = С++ 11 main.cpp):

main.cpp:19:4: error: call to deleted constructor of 'A'
        A b = f();
          ^   ~~~
main.cpp:8:2: note: function has been explicitly marked deleted here
        A(A&&) = delete;
        ^
1 error generated.
  • Должен ли компилятор использовать конструктор копирования, если конструктор перемещения явно удален?
  • Кто-нибудь знает, что использовать для "непередвижных" типов?

Спасибо заранее.

Ответ 1

A(A&&) = delete;

Объявление и определение его как delete все еще объявляет его и не делает его полностью несуществующим. Скорее, он схож (но не идентичен), объявляя его пустым и приватным. Например:

private: 
  A(A&&){}

На самом деле это был трюк, который иногда использовался для других операторов до того, как = delete был доступен. Опять же, он существует в смысле поиска, но вызов его никогда не разрешается, а в разрешениях на С++ вызывается (почти или во всех случаях) выполнение после всего остального, например, разрешение перегрузки, поиск имени.

Стандарт фактически говорит (8.4.3)

Удаленная функция неявно встроена.

И отмечая (что я нахожу), говоря, что удаленные функции не должны участвовать в поиске имен.

Кроме того, из 8.4.3

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

Ответ 2

Это немного исследовательская задача, но я думаю, что объявление конструктора перемещения утверждает, что конструктор перемещения должен быть рассмотрен. Когда он получает delete d, это означает, что объекты можно перемещать там, где они могут быть перемещены, если есть конструктор перемещения. Если вам нужен объект, который не перемещается, а копируется, вы просто объявляете конструктор копирования, и вы не будете упоминать конструктор перемещения.

Я не совсем нашел инструкцию в стандарте, но явным образом заявляю об этом выше, но в пункте 12.8 [class.copy] содержится примечание, содержащее следующую формулировку:

[Примечание. Если конструктор перемещения неявно объявлен или явно указан, выражения, которые в противном случае вызывают конструктор перемещения, могут вместо этого вызвать конструктор копирования. -end note]

Ответ 3

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

У вас есть два хода в коде. Во-первых, когда вы return a, потому что, когда возможно копирование, и объект, который будет скопирован, обозначается lvalue (a, здесь), он рассматривается как перемещение. Второй - в присваивании A b = f(), потому что a f() дает вам временное, которое еще не привязано к ссылке.

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

Ответ 4

Из рабочего проекта С++ 2012-11-02

8.4.3 Удаленные определения [dcl.fct.def.delete]
...
2 Программа, которая ссылается на удаленную функцию неявно или явно, кроме объявления ее, плохо сформирована. [Примечание: это включает вызов функции неявно или явно и формирование указателя или указателя на элемент к функции. Он применяется даже для ссылок в выражениях, которые потенциально не оцениваются. Если функция перегружен, он ссылается только в том случае, если функция выбрана с помощью разрешения перегрузки. - конечная нота]
...
4 Удаленная функция неявно встроена.

Поскольку ссылающийся конструктор перемещения ссылается, программа плохо сформирована.

"Использование" для недвижущегося типа может быть предотвращено перемещение и, таким образом, предотвратить возврат локального объекта. Я не видел такого использования сам, и я не знаю, имеет ли это смысл вообще, но YMMV.