为什么允许使用多余的类名限定符
Why are redundant class name qualifiers allowed?
我遇到了一些这样的代码:
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测试版编译和运行的,没有错误或警告/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) {
^
这种类型可以用多余的类限定来指代,这似乎很奇怪。
此外,VS11和clang/gcc对A::A(i)
的解析似乎有所不同。如果我执行A::A(b)
clang和gcc,则使用默认构造函数创建类型为A
的变量b
。VS11错误地指出b
是未知标识符。VS11似乎使用以i
为参数的构造函数A::A(int)
将A::A(i)
解析为临时A
的创建。当多余的限定符被消除时,VS像clang和gcc-do一样将源解析为变量声明,并产生关于隐藏变量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在其他人声明变量的地方构造临时变量。更糟糕的是,clang和gcc将
B b(A::A(i));
解析为最麻烦的解析,但VS认为这是用初始化器声明B
类型的变量b
。还有这么严重的差异吗 - 显然,在可移植代码中应该避免使用多余的限定符。有没有一个好的方法来防止这种构造被使用
虽然这种现象可能归因于类名注入,正如ephemient的回答中所指出的,但对于这个特定的例子,C++语言早就禁止了它。
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147
组合A::A
需要引用类构造函数,而不是类注入的名称。A::A(i)
应该被兼容的编译器解释为涉及构造函数名称的非法(因此毫无意义)表达式。例如,Comeau编译器会因为这个原因拒绝编译您的代码。
显然,VC11继续将A::A
视为对注入类名的引用。有趣的是,我在VS2005中没有观察到这个问题。
当A::A
被解释为引用注入的名称时,可以将A
对象声明为
A::A::A::A::A::A a;
等等,具有任意数量的A
s。但是不再是了。令人惊讶的是,ideone使用的GCC(4.3.4?)版本仍然存在问题
http://ideone.com/OkR0F
你可以在你的VC11版本中尝试一下,看看它是否允许这样做。
根据ISO/IEC 14882:2011最终草案§9,
²在看到类名之后,将类名称插入到其声明的作用域中。类名也被插入到类本身的作用域中,这被称为注入的类名
当你写
A::A(i);
它与申报相同
A i;
因为额外的括号是多余的(你可以随意添加),而A::A
指的是A
。
根据§14.6.1,
与普通(非模板)类一样,类模板有一个注入的类名(第9条)。注入的类名可以用作模板名或类型名。当它与模板参数列表一起使用时,作为模板的模板参数,或者作为友元类模板声明的精化类型说明符中的最终标识符时,它指的是类模板本身。否则,它等效于
<>
中包含的类模板的模板名称,后跟的模板参数。
注入的类名似乎是为了方便A<...>
在类中被简单地称为A
。
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- C++:TypeDef使用元组
- 使用std::multimap迭代器创建std::list
- 从不同线程使用int64的不同字节安全吗
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么在全局范围内使用"extern int a"似乎不行?
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用Google Mock来模拟gettimeofday()
- 如何使用默认参数等选择模板专业化
- 为什么使用 "this" 指针调用派生成员函数?
- 使用新行和不使用新行读取文件
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 为什么在C++使用常量函数时常量是多余的?
- 似乎多余的使用 cin 作为条件与 if,而
- 使用 linux epoll:epoll_ctl似乎有多余的参数?
- 仅使用 <iostream>、 <fstream> 和 <cstdlib>清除字符串中的任何多余空格
- 为什么允许使用多余的类名限定符
- 避免在使用ccache / clang编译Qt代码时出现多余的警告
- 如何在派生类中使用shared_from_this而无需多余的 RC 操作