Выдвижение функции элемента-члена
код:
#include <iostream>
#include <ios>
#include <string>
#include <type_traits>
#include <memory>
struct value
{
~value() = default;
std::unique_ptr<std::string> s;
};
int main()
{
std::cout << std::boolalpha;
std::cout << std::is_move_constructible<value>::value << '\n';
std::cout << std::is_move_assignable<value>::value << '\n';
using str_ptr = std::unique_ptr<std::string>;
std::cout << std::is_move_constructible<str_ptr>::value << '\n';
std::cout << std::is_move_assignable<str_ptr>::value << '\n';
return 0;
}
Выход (скомпилирован с g++ v4.7.2, http://ideone.com/CkW1tG):
false
false
true
true
Как я и ожидал, value
не перемещается конструктивно и не переносится назначаемым, потому что:
~value() = default;
- объявленный пользователем деструктор, который предотвращает неявное генерирование элементов перемещения в соответствии с разделом 12.8 (см. ниже).
Если деструктор удален, то value
перемещается конструктивно и перемещается назначаемым, как я ожидаю (http://ideone.com/VcR2eq).
Однако, когда определение value
изменяется на (http://ideone.com/M8LHEA):
struct value
{
~value() = default;
std::string s; // std::unique_ptr<> removed
};
вывод:
true
true
true
true
value
неожиданно перемещается конструктивно и переносится назначаемым.
Я недопонимаю или это ошибка компилятора?
Справочная информация. Я предоставил ответ на этот вопрос и был проинформирован о том, что Tree<>
был подвижным, но я не уверен, и я пытаюсь точно определить, или нет.
Раздел 8.4.2 Явно-дефолтные функции стандарта С++ 11 (черновик n3337):
Явно-дефолтные функции и неявно объявленные функции совместно именуемые дефолтными функциями, а реализация должна предоставить им неявные определения (12.1 12.4, 12.8), что может означать определяя их как удаленные. Специальная функция-член предоставляется пользователю, если она объявлена пользователем и явно не дефолт или не удаляется по его первой декларации. Предоставляемая пользователем функция с явно дефолтом (т.е. Явно дефолт после его первого объявления) определяется в точке, где она явно дефолт; если такая функция неявно определяется как удаленная, программа плохо сформирована. [Примечание. Объявление функции по умолчанию после ее первая декларация может обеспечить эффективное выполнение и краткое определение, в то время как позволяя стабильный двоичный интерфейс к развивающейся базе кода.-end note]
Раздел 12.8 Копирование и перемещение объектов класса (пункт 9):
If the definition of a class X does not explicitly declare a move constructor,
one will be implicitly declared as defaulted if and only if
- X does not have a user-declared copy constructor,
- X does not have a user-declared copy assignment operator,
- X does not have a user-declared move assignment operator,
- X does not have a user-declared destructor, and
- the move constructor would not be implicitly defined as deleted.
Ответы
Ответ 1
std::is_move_constructible<T>
истинно, если if std::is_constructible<T, T&&>
истинно, но это не означает, что такая конструкция вызовет конструктор перемещения, только то, что можно построить тип из r значения того же типа. Такая конструкция может использовать конструктор копирования.
Когда value::s
является unique_ptr
, конструктор копирования типа и оператор назначения копирования определяются как удаленные, так как член s
не может быть скопирован. У него нет конструктора перемещения и перемещения оператора присваивания, потому что, как вы указали, у него есть объявленный пользователем деструктор. Это означает, что у него нет конструктора копирования и конструктора перемещения (и никаких других определяемых пользователем конструкторов, которые могли бы принять аргумент типа value&&
), поэтому std::is_constructible<value, value&&>
является ложным.
Когда value::s
является string
, конструктор копирования типа и оператор присваивания копии не, определенные как удаленные, так как элемент s
является, и поэтому value
также можно копировать, а тип CopyConstructible также является MoveConstructible, потому что он действителен в этом контексте:
value v1;
value v2 = std::move(v1); // calls copy constructor
Это означает, что std::is_constructible<value, value&&>
истинно, даже если он вызывает конструктор копирования, а не конструктор перемещения.