复制省略并移动构造函数



考虑Person:的以下定义

struct Person
{
Person() { std::cout << "construct, "; }
Person(const Person&) { std::cout << "copyn"; }
Person(Person&&) { std::cout << "moven"; }
};

以及创建Person:的3种不同功能

Person create1()
{
std::cout << "create1: ";
Person p1{};
return p1;
}
Person create2()
{
std::cout << "create2: ";
if constexpr (true)
{
Person p1{};
return p1;
}
else
{
Person p2{};
return p2;
}
}

Person create3()
{
std::cout << "create3: ";
if constexpr (true)
{
return Person{};
}
else
{
return Person{};
}
}

最后,我调用create函数如下:

int main()
{
Person p1 = create1();
Person p2 = create2();
Person p3 = create3();
return 0;
}

输出为:

create1: construct
create2: construct, move
create3: construct

困扰我的是create2的输出。如果在create1create3中没有调用move构造函数,为什么在create2中调用它?

我使用的是GCC 12.0.0。

编辑:Clang 13.0.0不调用move构造函数。

我认为它是常量表达式中NRVO的一个实例,cppreference说:

在常量表达式和常量初始化中,保证返回值优化(RVO(,但禁止命名返回值优化

这与CWG 2278有关。

编辑

我重新考虑了一下:-(它是从main()调用的,所以它不可能是常量初始化。在实际的常量初始化中,gcc和clang都不执行NRVO。在本例中(参见戈德堡(:

struct Person {
bool was_moved;
constexpr Person() : was_moved{false} { }
constexpr Person(const Person&) : was_moved{false} { }
constexpr Person(Person&&) : was_moved{true} { }
};
constexpr Person create() {
if (true) {
Person p1;
return p1;
}
else {
Person p2;
return p2;
}
}
constexpr Person p = create();
int main() {
return p.was_moved;
}

CCD_ 8为1。

至于gcc为什么不在create2()中执行NRVO,一个答案可能是它不是强制性的,正如前面的cppreference链接所示。另一个可能是NRVO的gcc实现非常"简单";脆弱";。例如,当只涉及一个命名变量时,它就起作用:

Person create4() {
Person p{};
if  (true) {
return p;
}
else {
return p;
}
}

但当这整件事被放在一个子范围内时就停止了工作:

Person create5() {
{
Person p{};
if  (true) {
return p;
}
else {
return p;
}
}
}

(见戈德堡(

关于这两个问题,都有2012年的gcc错误:错误53637-NRVO不适用于涉及两个不同变量的情况。

相关内容

  • 没有找到相关文章

最新更新