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

时间:2018-04-20 12:15:27

标签: c++ c++11 c++14 move-semantics

来自N3337的

  

如果类X的定义没有明确声明移动   构造函数,只有当一个隐式声明为默认值   如果

     

X没有用户声明的复制构造函数,X没有   用户声明的复制赋值运算符,X没有   用户声明的移动赋值运算符,X没有   用户声明的析构函数,而移动构造函数不会   隐式定义为已删除。

第一个问题是:为什么编译器在不应该生成移动构造函数时(用户声明了析构函数和其他阻止生成移动构造函数的函数)。 下面的示例程序打印constructorCounter=5,这意味着使用了移动构造函数(没有移动操作值:: 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;
}

第二个相关问题:如果我禁用移动constructor array(array&&) = deleted;,为什么程序无法编译,为什么没有&#39;后退&#39;复制consturctor array(const array&)

2 个答案:

答案 0 :(得分:10)

首先,行:

array c(array(5));

导致使用的复制构造函数(因为没有用户声明的移动构造函数)。但是,由于恰好是c对象构造,编译器能够忽略副本并就地构建array对象。

现在当你 user-declare 移动构造函数为delete - d时,它会在重载解析期间被考虑,实际上在解析调用时:

array c(array(5));

array(array&&)将是一个完美的匹配...但它被删除,因此重载决议停止(完美匹配),并输出错误(该功能被删除)。

正如评论中所建议的那样,您是否一直在使用C ++ 17标准进行编译,即行

array c(array(5));

由于符合以下情况,因此会产生有保证的副本省略:

  

在初始化中,如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象

渲染代码严格等同于:

array c(5);

答案 1 :(得分:3)

此处不会生成移动构造函数。在这种情况下,不会声明移动构造函数和赋值。

您观察到的是编译器在array c(array(5));中进行复制。有关详细信息,请参阅copy elision

  

在初始化中,如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类的类相同,则初始化表达式用于初始化目标对象:

T x = T(T(T())); // only one call to default constructor of T, to initialize x

执行array(array&&) = deleted;时,上面的表达式现在考虑了移动构造函数(以前没有声明)并且因为它被删除而失败。