c++ - 为什么允许冗余类名限定符?

标签 c++ language-lawyer

我遇到了一些这样的代码:

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 is an unknown identifier。 VS11 似乎将 A::A(i) 解析为使用构造函数 A::A(int) 创建临时 A i 作为参数。当冗余限定符被消除时,VS 将源解析为变量声明,就像 clang 和 gcc 所做的那样,并产生关于隐藏变量 i 的类似错误。

解析中的这种差异解释了为什么 VS11 会阻塞多个额外的限定符; A::A::A::A(i),以及为什么,鉴于 clang 和 gcc 可以接受一个额外的限定符,任何多于一个的数字都与一个额外的结果相同。

这是另一个在不同上下文中使用冗余限定符的示例。所有编译器似乎都将其解析为临时构造:

class Foo {};

void bar(Foo const &) {}

int main() {
    bar(Foo::Foo());
}
  1. 为什么完全允许冗余限定符?
  2. 有一些上下文可以引用构造函数,例如继承构造函数的语法(class D : B { using B::B; };) 但VS似乎允许它任何地方。在如何解析冗余限定符方面,VS 是错误的吗?clang 和 gcc 是正确的吗?
  3. 我知道 VS 在符合标准方面仍然有点落后,但我确实感到有点惊讶,现代的、积极开发的编译器可能如此不同,在这种情况下将冗余限定符解析为构造函数的名称(即使构造函数没有名称)与将冗余限定符简单地解析为类型,导致 VS 构造一个临时变量,其他变量声明一个变量。更糟糕的是 B b(A::A(i)); 被 clang 和 gcc 解析为最令人烦恼的解析,但 VS 将其视为声明变量 b类型为 B 带有一个初始值设定项。这么严重的差距还有很多吗?
  4. 显然,在可移植代码中应避免冗余限定符。有什么好的方法可以防止使用此构造?

最佳答案

虽然这种现象可能归因于类名注入(inject),但正如 ephemient 的回答中所指出的,对于这个特定的例子,它在很久以前就被 C++ 语言禁止了。

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147

组合 A::A 需要引用类构造函数,而不是类注入(inject)名称。 A::A(i) 应该被兼容的编译器解释为涉及构造函数名称的非法(因此无意义)表达式。例如,Comeau 编译器将因此拒绝编译您的代码。

显然 VC11 继续将 A::A 视为对注入(inject)类名的引用。有趣的是,我在 VS2005 中没有观察到这个问题。

A::A 被解释为引用注入(inject)名称的那一天,我们可以将 A 对象声明为

A::A::A::A::A::A a;

等等,有任意数量的A。但现在不是了。令人惊讶的是,ideone使用的GCC版本(4.3.4?)仍然存在这个问题

http://ideone.com/OkR0F

你可以用你的 VC11 版本试试这个,看看它是否允许。

关于c++ - 为什么允许冗余类名限定符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11423380/

相关文章:

c++ - 获取pcap文件的第一包

c++ - 强制数组衰减(因为没有更好的标题)

c++ - 这种模板函数重载的案例让我无法理解

c++ - 组成一组容器类并从基访问它们

c++ - 获取光标下的控制权

c++ - 使用 MinGW-w64 为 Windows 编译 LAPACK

c++ - "Undefined Behavior"真的允许*任何*发生吗?

c++ - 显式默认的复制 ctor 生成比手写等效代码更好的代码

c++ - 如果实际对象是非常量,const_cast(this) 是否具有写操作未定义行为?

c++ - 如何组织 C++ 项目