c++ - NRVO 不应该保证本地命名变量和调用站点变量采用相同的地址吗?

标签 c++ nrvo

我认为应该这样做,因为它对正确性很重要。但是,我很惊讶地看到 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 定义了一个 move ctor 来检查 S(S&&) 是否被调用,如果没有,则应用 NRVO。

我们从 GCC 看到的结果是:

0x7ffc3ed7b5ac
0x7ffc3ed7b5ac
42

应用了 NRVO,它们采用相同的地址,这是预期的。

但是,Clang 的 output :

0x7fff908bbcc8
0x7fff908bbcf8
42

应用了 NRVO,但地址不同。

如果您想知道为什么拥有相同的地址很重要 - 这是因为某些对象可能会在构造时使用其地址进行一些注册,并且如果该对象被移动,则应该通知它(例如通过 move-ctor)。

已应用 NRVO 但具有不同的内存地址因此使其格式错误。 这明显违反了约定——没有调用自定义移动/复制构造函数,编译器如何将 S 的数据“复制”到不同的地方?

这是 Clang 中的错误吗?


如果我们向 S 添加一个析构函数,例如

~S() {}

This time , Clang 输出相同的地址。

最佳答案

肯定是cla​​ng的bug,他们应该是一样的,不然会出现下面这样的错误

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's rule is the one in the ABI: a class is passed indirectly if it has a non-trivial destructor or a non-trivial copy constructor. This rule definitely needs some adjustment [...]

事实上,如果我们在原始示例中为 S 定义一个非平凡的 dtor 或复制 ctor,我们会得到预期的结果(即相同的地址)。

关于c++ - NRVO 不应该保证本地命名变量和调用站点变量采用相同的地址吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44997760/

相关文章:

c++ - 迭代 C++ 列表中一对(或元组)的特定项

c++ - 为什么要依赖命名返回值优化?

c++ - NRV(命名返回值)复制构造函数

C++ 协方差意外行为

c - 在纯 C 的 gcc 中禁用命名返回值优化

C++ 返回值优化

c++ - 关于复制构造函数和 NRVO

c++ - 如果没有找到 map 元素会返回什么?

c++ - 如何使用 C++ 将字符串附加到 HDF5 数据集?

c++ - 编译器错误 - opencv2/highgui.hpp : No such file or directory