代码:
#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
意外地移动可构造并移动可分配。
我误解了还是编译器错误?
背景:我提供了对this问题的回答,并被告知Tree<>
是可移动的,但我不确定并且正在尝试确定它是否是或不
部分 8.4.2 c ++ 11标准的显式默认函数(草案n3337):
明确默认的函数和隐式声明的函数 统称为违约函数,并实施 为它们提供隐含定义(12.1 12.4,12.8),这可能意味着 将它们定义为已删除。 如果是用户声明的特殊成员函数,则由用户提供 未在第一次声明中明确默认或删除。 用户提供的明确默认功能(即明确默认) 在第一次声明之后)定义在它的位置 明确违约;如果将这样的函数隐式定义为已删除, 该计划格式不正确。 [注意:声明函数默认后的默认值 第一个声明可以提供有效的执行和简洁的定义 为稳定的代码库提供稳定的二进制接口。-end note]
Section 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.
答案 0 :(得分:9)
std::is_move_constructible<T>
为真,则 std::is_constructible<T, T&&>
为真,但这并不意味着这样的构造将调用移动构造函数,只是可以从相同的rvalue构造类型类型。这样的构造可能使用复制构造函数。
当value::s
为unique_ptr
时,类型的复制构造函数和复制赋值运算符被定义为已删除,因为s
成员不可复制。它没有移动构造函数和移动赋值运算符,因为正如您所指出的,它具有用户声明的析构函数。这意味着它没有复制构造函数,也没有移动构造函数(也没有其他用户定义的构造函数可以接受value&&
类型的参数),因此std::is_constructible<value, value&&>
为假。
当value::s
为string
时,类型的复制构造函数和复制赋值运算符不定义为已删除,因为s
成员为< / strong>可复制,因此value
也是可复制的,而CopyConstructible类型也是MoveConstructible,因为它在此上下文中有效:
value v1;
value v2 = std::move(v1); // calls copy constructor
这意味着std::is_constructible<value, value&&>
为真,即使它调用复制构造函数而不是移动构造函数。