4

考虑以下代码片段:

#include <iostream>
#include <string>

class A { 
 public:
    A() { 
        std::cout << "A::A()\n";
    } 

    ~A() { 
        std::cout << "A::~A()\n";
    } 

    A(const A&) = delete;

    A(A&&) { 
        std::cout << "A::A(A&&)\n";
    };

};

A f() { 
    A a;
    return a;
}

int main() { 
    A a = f();
    return 0;
}

g++它用and编译得很好,clang++输出是

A::A()
A::~A()

在这种情况下,RVO 似乎发挥了作用。请注意,没有调用移动构造函数。

但是,如果从上面的代码中删除那个未使用的移动构造函数并且代码片段变成这样:

#include <iostream>
#include <string>

class A { 
 public:
    A() { 
        std::cout << "A::A()\n";
    } 

    ~A() { 
        std::cout << "A::~A()\n";
    } 

    A(const A&) = delete;

};

A f() { 
    A a;
    return a;
}

int main() { 
    A a = f();
    return 0;
}

两者都clang++拒绝g++编译,因为类的复制构造函数A被标记为已删除,所以似乎没有发生 RVO。

删除未使用的移动构造函数如何导致这种情况?

4

3 回答 3

4

请注意,在复制省略优化中,复制/移动构造函数仍然必须存在且可访问。并且不能保证在每种情况下都会执行复制省略。

(强调我的)

即使发生复制省略并且未调用复制/移动构造函数,它也必须存在且可访问(就好像根本没有发生优化一样),否则程序是错误的。

对于您的代码,复制构造函数已被编辑,如果您删除了移动构造函数的定义,并且由于类具有用户定义的析构函数delete而不会隐式声明,则移动/复制构造函数都不存在且可访问A,这就是编译失败的原因。

总之,这里需要复制/移动语法,编译器会检查它。然后编译器将决定是否执行复制省略(例如在调试模式下)。

顺便说一句:您可以使用-fno-elide-constructorsclang 和 gcc 来禁止它。

于 2016-06-01T16:10:22.567 回答
3

您需要记住,(N)RVO 是一种优化。即使它启动,代码也应该符合标准,即使用复制(或移动)构造函数构造值。即使构造函数最终没有被调用,它也必须是可用的。

如果由于优化而不会调用它,则有一个建议允许缺少/不可用的构造函数,但我怀疑它会被实现。

于 2016-06-01T16:05:14.230 回答
-3

如果你有复制构造函数,如果我没记错的话,你也必须有移动构造函数。

于 2016-06-01T16:10:46.060 回答