为什么名称的声明点在其初始值设定项之前?

Why is a name's point of declaration before its initializer?

C++ 标准说:

The point of declaration for a name is immediately after its complete declarator and before its initializer (if any)... [basic.scope.pdecl]

也就是说,一个变量在范围内,并且可以在它自己的初始化表达式的上下文中被引用。

据我所知,您可以使用它执行以下类型的操作:

  1. int x = x,格式正确但无意义。
  2. void* p = &p,可爱但没用
  3. std::any a {&a},#2 的 C++17 版本。
  4. MyClass m {std::move(m)},#1 的 C++11 版本和可能的 UB 不知何故
  5. MyClass m {myFunc(m)},有一个函数接受你未初始化的对象,我猜它记录在某处?和 returns 返回一些值,以便构造函数可以试一试。

#1-4当然没用了。似乎可以构建一个界面,其中 #5 有一定意义,但我认为这不是完成任何事情的最直接方式。由于在评估初始化程序时新变量尚未初始化,因此 useless/illegal 读取其值,并且其地址通常对其初始化并不重要。

(可以 稍微 更强的情况下将声明点保留到初始化程序之后:int avg = avg(a,b,c)。那不是好的代码,对于任何东西,但它比 void* p = &p 更有意义。)

但这不仅仅是用例。通常,C++ 会尽力防止访问未初始化的对象。例如,它一次建立一个对象的 vtable 基数 class:如果 D 继承自 C 继承自 B,在 C 的构造函数期间,虚拟方法将被分派给 C 的实现,而不是 D 的。可以窥视未初始化对象的常见情况是这种情况,以及(更常见的问题)在成员初始化表达式中使用 this

所以我看不到在其初始化程序之前将名称引入作用域的用途,并且我可以看到将其延迟到初始化程序之后的明确理由(Stroustrup 也会看到)。鉴于此,是否明确指出了 C++ 的选择行为?

its address isn't generally going to be important to its initialization.

template <class T>
struct Node
{
  Node *prev, *next;
  std::optional<T> data;
};

template <class T>
struct List
{
    Node<int> sentinel {&sentinel, &sentinel};
    // methods ...
};

这是一个完全合法且有用的sentinel-based双向链表初始化。

任何graph-like数据结构都可以有自引用,这可以通过将对象的指针或引用作为参数传递给它自己的构造函数来实现。没有理由禁止这些。

如果您担心 int x = x; 中固有的未定义行为,只需 无论如何您都应该这样做。编译器现在相当擅长捕捉这些东西。