【发布时间】:2020-10-21 10:06:44
【问题描述】:
说 C++ 标准:
名称的声明点在其完整声明符之后和其之前 初始化程序(如果有)... [basic.scope.pdecl]
也就是说,一个变量在它自己的初始化表达式的上下文中,并且可以被引用。
据我所知,你可以用它做以下类型的事情:
-
int x = x,格式正确但毫无意义。 -
void* p = &p,可爱但没用。 -
std::any a {&a},#2 的 C++17 版本。 -
MyClass m {std::move(m)},#1 的 C++11 版本,可能是 UB某种方式。 -
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++ 选择的行为是否明确?
【问题讨论】:
-
我并不是说这是 C 的基本原理,而是
int *p = malloc(5 * sizeof(*p));。 -
有趣的问题。我认为标准引用的句子并不仅仅将声明限制为变量。您可以轻松合法地声明一个函数
template <typename T> auto foo() -> decltype(T::foo());我不确定这在哪里有用,但它是有效的。 -
int* arr = malloc( 10 * sizeof(*arr) );是一个有效的用例。在 Windows API 中,您有时会在结构中存储结构的大小。所以,它确实有它的用例。 -
@AyxanHaqverdili Chris 提到了同样的事情。但是,在 winapi 的情况下,我看到的所有示例代码都使用
sizeof(TYPE),而不是sizeof(obj)。不是必要的用例,除非需要向后兼容。 -
@Sneftel 你是什么意思它只需要向后兼容?如果您今天正在编写 win32 代码,您仍然会做类似的事情。我喜欢
sizeof(obj),因为您不需要重复类型名称。我并不是说规则是好的。我只是说它确实有一些用例。
标签: c++ language-lawyer