奇数C++命名空间解析怪癖和 G++ 与 CLang++
odd C++ namespace resolution quirk and g++ vs clang++
这始于一个观察。 我更改了一些看起来有点像这样的代码(编辑:我在这里删除了指定的初始值设定项,它们也不在原始代码中):
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
不再是一个普通的struct
,而是一个模板化的,之后它就停止了工作。 没什么大不了的,但它让我很好奇,所以我做了实验。
如果我们将其更改为非 POD 类型:
struct S {
S() { std::cout << "made an S" << std::endl; }
enum E { E1, E2 } member;
}
(使用适当的#include
)不再允许。 Clang 和 g++ 产生不同的诊断。 这是叮当的抱怨:
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 Sn"; }
^
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 Sn"; }
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
初始值设定项发出了投诉 clang,但此处没有构造任何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;
^
哪个编译器是正确的,为什么? 这个"资格过高"的名字到底有什么规则?
> Yakk 已经解决了您问题的指定初始值设定项部分。我将解决您问题的最后一部分。S::S::S var
(在这种情况下)有效吗?否,每个类.qual#2:
在不忽略函数名称34和 嵌套名称说明符指定一个类 C:
如果在 C 中查找时,嵌套名称说明符后指定的名称是 C 的注入类名(子句 [class]),或者
在作为成员声明的 using-声明符中,如果在 嵌套名称说明符与标识符或 简单模板 ID 的模板名称在 嵌套名称说明符,
相反,该名称被视为命名类 C 的构造函数。
为了使它有效,您需要明确说struct S::S::S var
.所以 clang 3.9 是错误的。
此外,Rob的评论在这里无关紧要。S::S
只是在类定义中查找时注入的类名。
指定的初始值设定项是在 c++2a 中C++的 C 功能。 它们是C++模式下的常见扩展。 显然,在这一点上,它们仅限于 pod(类 C)类型。 毫不奇怪,他们打破了。
通常,不同的编译器可能会为不正确的代码产生不同的错误,尤其是在使用C++扩展时。 C++标准从未强制要求诊断的内容(可能有例外,但我不记得是什么)。
GCC 有一个扩展,可以让你命名对象构造函数。 这与结构命名空间的病理使用相冲突。
- 奇怪的结构&GCC&clang(void*返回类型)
- 数据成员SFINAE的C++17测试:gcc vs clang
- 当我编译webrtc服务器时,Windows上只支持clang-cl
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 我可以将一个用clang c++11编译的对象与另一个用c++17编译的对象链接起来吗
- Clang bug?使用指针作为模板参数
- clang整洁10忽略了我的NOLINT命令
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 在clang++预处理器中确定gcc工具链版本
- 为什么 Clang 不允许"and"作为函数名称?
- 带有 -stdlib=libc++ 的 clang++ 9.0.1 找不到<optional>
- clang格式:宏的缩进
- CLANG 编译器 说:变量"PTR"可能未初始化
- clang格式:禁用排序包含
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- gcc和clang在表达式是否为常量求值的问题上存在分歧
- 循环展开 - G++ 与 Clang++
- 如何使用Clang/GCC在Mac上为C/C++设置VSCode
- 一位朋友将模板函数缩写为clang和gcc
- 类模板参数推导-clang和gcc不同