提问者:小点点

编译器在不应该生成默认移动构造函数时生成默认移动构造函数


从 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


共2个答案

匿名用户

起初,该行:

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