【发布时间】:2018-01-06 01:09:14
【问题描述】:
这始于一次观察。我更改了一些看起来有点像这样的代码(编辑:我在这里取出了指定的初始化程序,它们也不在原始代码中):
struct S {
enum E { E1, E2 } member;
}
// file1.cc
S v1 = { S::E1 };
// file2.cc
S v2 = { S::S::E2 };
请注意,file2.cc 过度限定了E2。然而,这在 g++ 和 clang++ 中都有效。 (编辑 2:这个特定 VM 上的 g++ 是 g++-5.4.1,但原始代码经过了早期和后来的 g++ 版本,以及多个 clang 版本。)事实上,我们可以这样写:
S v3 = { S::S::S::S::S::S::S::E1 };
(无论我们喜欢多少S::s),无论我们喜欢什么。我改变了一些东西,使S 不再是一个普通的struct,而是一个模板化的,之后它就停止工作了。没什么大不了的,但它让我很好奇,所以我做了实验。
如果我们将其更改为非 POD 类型:
struct S {
S() { std::cout << "made an S" << std::endl; }
enum E { E1, E2 } member;
}
(使用适当的#include)不再允许。 Clang 和 g++ 产生不同的诊断。这是 clang 的抱怨:
namespace.cc:8:3: error: no matching constructor for initialization of 'S'
S x = { .member = S::S::E1 };
namespace.cc:3:8: note: candidate constructor (the implicit copy constructor)
not viable: cannot convert argument of incomplete type 'void' to
'const S &' for 1st argument
struct S {
^
namespace.cc:3:8: note: candidate constructor (the implicit move constructor)
not viable: cannot convert argument of incomplete type 'void' to 'S &&'
for 1st argument
struct S {
^
namespace.cc:4:3: note: candidate constructor not viable: requires 0 arguments,
but 1 was provided
S() { std::cout << "made an S\n"; }
^
1 error generated.
和 g++ 的:
namespace.cc:8:28: error: could not convert ‘{E1}’ from ‘<brace-enclosed initializer list>’ to ‘S’
S x = { .member = S::S::E1 };
这些似乎遵循不同的规则。这是怎么回事?
接下来,让我们尝试另一种滥用。这是整个程序:
#include <iostream>
struct S {
S() { std::cout << "made an S\n"; }
enum E { E1, E2 } member;
};
int main() {
std::cout << S::S::S::S::S::E1 << std::endl;
#ifdef DECL
S::S::S var;
#endif
return 0;
}
这段代码在两个编译器中都可以编译(没有-DDECL):
$ clang++-3.9 -std=c++11 -Wall -O namespace.cc
$ ./a.out
0
$ g++ -Wall -std=c++11 -O namespace.cc
$ ./a.out
0
(尽管早期代码中的变量 member 初始化程序发出了抱怨,但这里没有构造 S。)但是,启用 main 中的变量会导致 g++ 失败,但不会导致 clang :
$ clang++-3.9 -std=c++11 -DDECL -Wall -O namespace.cc
$ ./a.out
0
made an S
$ g++ -std=c++11 -DDECL -Wall -O namespace.cc
namespace.cc: In function ‘int main()’:
namespace.cc:11:3: error: ‘S::S’ names the constructor, not the type
S::S::S var;
^
namespace.cc:11:11: error: expected ‘;’ before ‘var’
S::S::S var;
^
namespace.cc:11:14: error: statement cannot resolve address of overloaded function
S::S::S var;
^
哪个编译器是正确的,为什么?究竟是这个“过度限定”名称的规则是什么?
【问题讨论】:
-
它们最终也是 C++20 的一个特性,但它只是 2018 年,所以它们不是标准的。
-
请注意,上面提到的 clang 错误据说已在 clang 5 中修复,实际上 clang 5 并没有重现该差异。 wandbox.org/permlink/cwYV0XhkzouaHBxf
-
顺便说一句,"如果我们将其更改为非 POD 类型:" 您在此处禁用了聚合初始化,因此您实际上需要一个匹配的构造函数。
-
“什么规则,如果有的话,允许这种过度资格,什么时候?” - 第 9 条,
[class],第 2 段,“class-name 也被插入到类本身的范围内;这被称为 injected-class-name. 出于访问检查的目的,注入的类名被视为公共成员名。” - C++03
标签: c++ c++11 language-lawyer