NRVO不应该保证局部命名变量和调用站点变量采用相同的地址吗?
Shouldn't NRVO guarantee the local named variable and the call-site variable to take the same address?
我认为应该这样做,因为它对正确性很重要。但是,我很惊讶地看到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。
我们从 GCC 看到的结果是:
0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42
应用 NRVO 并且它们采用相同的地址,这是预期的。
但是,Clang的输出:
0x7fff908bbcc8
0x7fff908bbcf8
42
已应用 NRVO,但地址不同。
如果你想知道为什么拥有相同的地址很重要 - 这是因为某些对象可能会在构造时对其地址进行一些注册,如果对象被移动,则应通知它(例如通过 move-ctor(。
因此,应用了 NRVO 但具有不同的内存地址,使其格式不正确。 这显然违反了合同 - 没有调用自定义移动/复制 ctor,编译器如何将 S 的数据"复制"到不同的地方?
这是 Clang 中的错误吗?
如果我们向S
添加一个析构函数,例如
~S() {}
这一次,Clang 输出相同的地址。
绝对似乎是 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;
};
其中需要移动(或地址不更改的省略(以确保内部指针指向正确的整数。 但是由于省略,该指针指向不包含成员整数的内存。
在此处查看输出 https://wandbox.org/permlink/NgNR0mupCfnnmlhK
正如@T.C.所指出的,这实际上是Itanium ABI规范中的一个错误,没有考虑move-ctor。引用 Clang 的开发:
Clang 的规则是 ABI 中的规则:如果一个类是间接传递的,则 具有非平凡析构函数或非平凡复制构造函数。这 规则肯定需要一些调整[...]
事实上,如果我们在原始示例中为S
定义一个非平凡的 dtor 或 copy-ctor,我们会得到预期的结果(即相同的地址(。
- 附加调试器并以编程方式获取变量地址 Visual Studio
- 变量地址的运算符[]是如何工作的
- C 给出了一个字符串列表,如何从类中获取各个变量地址
- 从函数返回变量地址时如何修复"与局部变量关联的堆栈内存地址"?
- 获取封装在命名空间中的静态变量地址
- 为什么分配的变量地址之间相差 16 个字节?
- 为什么在循环中定义的变量地址在每次迭代中都保持不变
- 是否可以使用指向语句中声明的变量地址的指针"if"
- 字符串从变量(&变量)地址的长度 - 嵌入式C
- 汇编:C++堆栈变量地址不同/错误?
- x 在变量地址中的意义是什么?
- 在哪里放置 HW BP 以捕获全局变量地址损坏
- C++变量地址不匹配
- 重载 std::ostream 运算符<<未调用,stream 获取变量地址而不是对象
- std矢量包含指针和变量地址.清理
- 注册变量地址
- 使用内联程序集获取变量地址
- 如果用调试信息编译,则通过其名称获取全局变量地址
- 如何在Visual Studio中复制变量地址
- 当堆栈内存中的变量地址分配给数据段或堆内存中的指针时,是否有任何错误?