如果声明引用指向自身怎么办?例如。整数& x = x;

What if a reference is declared to point to itself? E.g. int& x = x;

以下代码在 g++ 版本 10.1.0 中编译成功:

int main()
{
    int& x = x;
    return x;
}

编译器甚至为此定义了一个警告,表明它是故意的,它不是错误。当使用 -Wall 编译时,它 returns 输出如下:

<stdin>: In function ‘int main()’:
<stdin>:3:14: warning: reference ‘x’ is initialized with itself [-Winit-self]
    3 |         int& x = x;
      |              ^
<stdin>:3:14: warning: ‘x’ is used uninitialized in this function [-Wuninitialized]
    3 |         int& x = x;
      |

我的测试表明,这会导致对地址 0 处的值的引用,就好像它被声明为 int& x = *(int*)nullptr; 一样。如果 x 引用的值被使用过,这自然会导致段错误。

我可以想到一个(有问题的)情况,这可能会有用:如果你想在 class 上调用一个没有可访问构造函数的方法,而该方法不是 static但仍然没有使用 class 的数据:

struct S {
    S() = delete;
    int value() { return 69; }
};

int main()
{
    S& s = s;
    return s.value();
}

但是还有其他方法可以做到这一点,这些方法甚至不会用 -Wall 发出警告并且不太可能是未定义的行为。 (例如,((S*)0)->value()。)

所以我要问的是:这实际上是未定义的行为吗?它看起来确实是这样。有没有任何可以想象的情况,在这种情况下,自我参照是最好的解决方案?如果不是,为什么默认情况下会抑制警告?

接受此代码的原因是因为 C++ 语法允许这样做;不是因为有任何实际原因。

在C++语法中,定义的第一部分引入了名称标识符,它可能在其余范围内,包括在同一语句中。这就是允许您拥有如下代码的原因:

int x = 1, &y = x;

第一个 int x 产生的名字 x 可以在任何地方使用。这有也允许self-assignment的side-effect,如:

int x = x;

这个赋值实际上是未定义的行为,因为 x 还不是初始化对象,因此正在从未初始化的数据(即 UB)中读取。

引用self-assigned也是如此; int& x 引入名称 x,而 = x 绑定到可绑定到引用的类型(也恰好是 int& x)。此引用在分配时尚未初始化,因此此访问是未定义的行为。

编译器可以自由地处理未定义的行为,但它会选择 - 看起来您正在观察 gcc 将引用的地址视为 0x0。这不是保证的行为,可能会受到编译器版本、优化级别等的影响。


这没有真正的实际目的;但是使用像 auto 这样的现代实践很容易解决问题,因为在 self-construction 中使用 auto 的代码将无法推断出底层类型:

auto x = x;  // error, cannot deduce x
auto& y = y; // error, cannot deduce y

why is the warning suppressed by default?

C++ 标准仅指示少数需要诊断消息的错误。许多错误属于“不需要诊断”,编译器可以选择通过可选标志添加警告。这取决于实现的自由裁量权,并且是编译器作者的问题。

大多数未初始化的访问错误不需要诊断,我假设这属于诊断。


至于您的 questionable-reason 使用 self-reference:这将是未定义的行为,就像您的示例 ((S*)0)->value().

由于引用是从 UB 构造的,因此从它访问任何成员也是 UB。

对于您的另一个示例,无论该成员函数是否访问任何成员状态,或者它是否被内联,对空指针的任何形式的成员访问都是未定义的行为。这并不意味着在正确的编译器上使用正确的编译器标志可能无法 工作 ;但它不是便携、可靠或安全的解决方案。