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

Sne*_*tel 14 c++ language-lawyer

Sayeth C++ 标准:

声明的点的名称是立即其完整的说明符后,其之前 初始化(如果有的话)... [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)}中,C ++ 11版本的#1和可能UB莫名其妙
  5. MyClass m {myFunc(m)},用一个函数来获取你未初始化的对象,我想把它记录在某个地方?并返回一些值,以便构造函数可以试一试。

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

(对于将声明点保留到初始化器之后,可以提出一个稍微强一点的案例:int avg = avg(a,b,c)。这不是好的代码,它对任何事情都不是必需的,但它比void* p = &p.更有意义。)

但这不仅仅是关于用例。通常,C++ 会煞费苦心地阻止访问未初始化的对象。例如,它一次设置一个对象的 vtable 一个基类:如果 D 继承自 C 继承自 B,则在 C 的构造函数期间,虚方法将被分派给 C 的实现,而不是 D 的实现。可以查看未初始化对象的常见情况是这种情况,以及(更常见的问题)this在成员初始值设定项表达式中的使用。

因此,我看不到在其初始化程序之前将名称带入范围的用途,并且我可以看到将其延迟到初始化程序之后的明确理由(Stroustrup 也会看到)。鉴于此,C++ 选择的行为是否有明确的点?

n. *_* m. 17

它的地址通常对其初始化并不重要。

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

template <class T>
struct List
{
    Node<int> sentinel {&sentinel, &sentinel};
    // methods ...
};
Run Code Online (Sandbox Code Playgroud)

这是基于哨兵的双向链表的完全合法且有用的初始化。

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

如果您担心 中固有的未定义行为int x = x;,只需启用警告(警告:无耻插件),就像您应该做的那样。编译器现在相当擅长捕捉这些东西。