我认为应该,因为这对正确性很重要。但是,我很惊讶地看到了Clang的输出。请考虑以下代码:
#include <iostream>
struct S
{
int i;
S(int i) : i(i) {}
S(S&&)
{
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
S f()
{
S s{42};
std::cout << &s << "\n";
return s;
}
int main()
{
S s{f()};
std::cout << &s << "\n";
std::cout << s.i << "\n";
}
我们为S
定义了一个移动ctor来检查S(S&&)
是否被调用,如果没有,则应用NRVO。
我们从海湾合作委员会看到的结果是:
0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42
应用NRVO并且它们采用相同的地址,这是预期的。
然而,Clang的output:
0x7fff908bbcc8
0x7fff908bbcf8
42
应用NRVO但地址不同。
如果您想知道为什么具有相同的地址很重要 - 这是因为某个对象可能会在构造时对其地址进行一些注册,如果对象被移动,则应该通知它(例如通过move-ctor)。
应用NRVO但具有不同的存储器地址因此使其形成错误。 这明显违反了合同 - 没有调用自定义移动/复制ctor,编译器如何将S的数据“复制”到另一个地方?
这是Clang的错误吗?
如果我们向S
添加析构函数,例如
~S() {}
This time,Clang输出相同的地址。
答案 0 :(得分:5)
肯定似乎是clang中的错误,它们应该是相同的,否则以下内容将是错误的
struct S
{
int i;
int* ptr;
S(int i) : i(i) {
this->ptr = &this->i;
}
S(S&& s)
{
this->i = s.i;
this->ptr = &this->i;
std::cout << "S(S&&)\n";
}
S(S const&) = delete;
};
需要移动(或地址不变的省略)以确保内部指针指向正确的整数。但由于elision指针指向不包含成员整数的内存。
请参阅此处的输出https://wandbox.org/permlink/NgNR0mupCfnnmlhK
正如@ T.C.所指出的,这实际上是Itanium ABI规范中的一个错误,它不会考虑move-ctor。引自Clang的开发者:
Clang的规则是ABI中的规则:如果它是间接传递的 有一个非平凡的析构函数或一个非平凡的复制构造函数。这个 规则肯定需要一些调整[...]
实际上,如果我们在原始示例中为S
定义了一个非平凡的dtor或copy-ctor,我们就会得到预期的结果(即相同的地址)。