从 N3337:
如果类X的定义没有显式声明移动构造函数,则当且仅当
X没有用户声明的复制构造函数,X没有用户声明的复制赋值操作符,X没有用户声明的移动赋值操作符,X没有用户声明的析构函数,并且移动构造函数不会被隐式定义为deleted。
第一个问题是:为什么编译器不应该生成移动构造函数(有用户声明的析构函数和其他函数阻止移动构造函数的生成)。下面的示例程序打印构造计数器=5
这意味着使用了移动构造函数(没有移动操作value::constructorCounter应该是10)
#include <iostream>
class value {
public:
value() {
++constructorCounter;
}
value(const int value)
: _value(value)
{
++constructorCounter;
}
value(const value& other)
: _value(other._value)
{
++constructorCounter;
}
const value& operator=(const value& rhs) {
_value = rhs._value;
return _value;
}
~value() { }
static int constructorCounter;
private:
int _value;
};
int value::constructorCounter = 0;
class array {
public:
// array() = delete;
// array(array&&) = delete;
array(const int size)
: _size(size), _values(new value[size])
{
std::clog << "array(const int size)" << std::endl;
}
array(const array& rhs)
: array(rhs._size)
{
std::clog << "array(const array& rhs)" << std::endl;
for (int i = 0; i < _size; ++i)
_values[i] = rhs._values[i];
}
array& operator=(const array&) {
std::clog << "array& operator=(const array&)" << std::endl;
}
~array() {
delete [] _values;
}
private:
value* _values;
int _size;
};
int main(int argc, char *argv[]) {
array c(array(5));
std::clog << "constructor counter=" << value::constructorCounter << std::endl;
return 0;
}
第二个相关问题:如果我禁用了move 构造函数数组(array
起初,该行:
array c(array(5));
导致复制构造函数被使用(因为没有用户声明的移动构造函数)。然而,由于它恰好是< code>c对象构造,编译器能够省略复制并就地构造您的< code>array对象。
现在,当用户将move构造函数声明为< code>delete-d时,在重载解析过程中,甚至在解析行中的调用时,都会考虑它:
array c(array(5));
<代码>数组(数组
正如评论中所建议的,你会使用C 17标准来编译吗?
array c(array(5));
会导致保证的副本省略,因为它适合以下场景:
在初始化中,如果初始化表达式是一个prvalue,并且源类型的cv非限定版本与目标对象的类是同一个类,则初始化表达式用于初始化目标对象
呈现代码严格等效于:
array c(5);
这里不生成移动构造函数。学究式地,在这种情况下没有声明move构造函数和赋值。
您观察到的是编译器消除了在数组c(数组(5))中的复制;
。有关更多详细信息,请参阅复制省略:
在初始化中,如果初始值设定项表达式是 prvalue,并且源类型的 cv 非限定版本与目标的类相同,则初始值设定项表达式用于初始化目标对象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
当您执行<code>array(array