【发布时间】:2023-04-07 11:01:01
【问题描述】:
我遇到了一些这样的代码:
struct A {
A() {}
A(int) {}
};
struct B : A {
void init(int i);
};
void B::init(int i) {
A::A(i); // what is this?
}
int main() {
B b;
b.init(2);
}
这使用 VC11 beta 编译和运行,没有 /W4 的错误或警告。
明显的意图是调用 B::init 来重新初始化 B 的 A 基础子对象。我相信它实际上解析为一个名为i 类型为A 的新变量的变量声明。使用 clang 编译会产生诊断:
ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable
A::A(i);
^
ConsoleApplication1.cpp:10:22: note: previous declaration is here
void B::init(int i) {
^
ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type
A::A(i);
^
ConsoleApplication1.cpp:10:22: note: previous definition is here
void B::init(int i) {
^
这个类型可以用多余的类限定来引用,这似乎很奇怪。
另外,A::A(i) 似乎被 VS11 和 clang/gcc 解析不同。如果我做A::A(b) clang 和 gcc 使用默认构造函数创建一个类型为A 的变量b。 VS11 错误指出 b 是未知标识符。 VS11 似乎将A::A(i) 解析为使用构造函数A::A(int) 以i 作为参数创建临时A。当多余的限定符被消除时,VS 像 clang 和 gcc 一样将源解析为变量声明,并产生关于隐藏变量 i 的类似错误。
这种解析上的差异解释了为什么 VS11 会阻塞多个额外的限定符; A::A::A::A(i),以及为什么鉴于 clang 和 gcc 可以接受一个额外的限定符,任何多于一个的数字与一个额外的结果相同。
这是另一个在不同上下文中使用冗余限定符的示例。所有编译器似乎都将其解析为临时构造:
class Foo {};
void bar(Foo const &) {}
int main() {
bar(Foo::Foo());
}
- 为什么完全允许冗余限定符?
- 在某些上下文中可以引用构造函数,例如继承构造函数的语法 (
class D : B { using B::B; };),但 VS 似乎允许它在任何地方使用。 VS 错了吗,clang 和 gcc 在冗余限定符的解析方式上是否正确? - 我知道 VS 在标准合规性方面仍然落后了一些,但我确实发现现代、积极开发的编译器可能如此不同,在这种情况下将冗余限定符解析为构造函数的名称,这有点令人惊讶(即使构造函数没有名称)与简单地将冗余限定符解析为类型,导致 VS 在其他人声明变量的地方构造一个临时变量。如果
B b(A::A(i));被 clang 和 gcc 解析为最令人头疼的解析,情况会变得更糟,但 VS 将其视为使用初始化程序声明B类型的变量b。这么严重的差异还有很多吗? - 显然,在可移植代码中应避免冗余限定符。有什么好的方法可以防止这种结构被使用吗?
【问题讨论】:
-
FWIW,Comeau 同意 MSVC:
A::A(j)是一个构造函数调用。 IIRC 这是基于名称A被注入到A的类命名空间中,并且引用了A类型(不是构造函数)。但是TypeIdentifier(j)只是编写 C 风格转换(TypeIdentifier)j的另一种方式,因此它实际上调用了构造函数。此外,Comeau 报告“构造函数或析构函数可能没有获取其地址”,我不明白。 -
@SteveJessop 但是如果
A::A指的是注入到类命名空间A中的类型名称A不是A::A指的是类型吗?然后A::A i;必须是变量声明,A::A(i);也必须是变量声明? -
啊,因为如果有疑问,这是一个声明。好吧,我当然很困惑。如果/Comeau 弄错了,我会感到惊讶。
标签: c++ language-lawyer