最新标准已修改,增加了 P1787 的内容。关于可能发生冲突的两个声明的一个规则是:
如果两个声明对应并导致它们的共享名称表示不同的实体([basic.link]),则它们可能会发生冲突。
我不知道这里“不同实体”的措辞是什么意思。这是否意味着这些实体具有不同的种类或意味着它们是由basic.link#8确定的不同实体?实体有以下几种:
实体是值、对象、引用、结构化绑定、函数、枚举器、类型、类成员、位域、模板、模板特化、命名空间或包。
例如,
void fun(); //#1
extern int fun; //#2
int main(){}
如果“不同实体”一词是指前者,那么#1 表示函数,#2 表示变量,它们是不同类型的实体。并且这样的两个声明在同一范围内,因此它们的格式不正确:
如果在任何范围内,一个名称绑定到两个可能冲突的声明并且一个在另一个之前,则该程序是非良构的
但是,如果differenct entities
应该由 [basic.link#8] 确定,而不是根据实体的种类来确定。那么,这也是有道理的。因为,根据 [basic.link#8]
两个实体声明声明同一个实体,如果考虑到未命名类型的声明以引入它们的名称以用于链接目的,如果有的话([dcl.typedef],[dcl.enum]),它们对应([basic.scope.scope]) , 具有相同的目标范围,不是函数或模板参数范围,并且要么
- 它们出现在同一个翻译单元中,或者
- 它们都使用模块链接声明名称并附加到同一个模块,或者
- 它们都声明具有外部链接的名称。
由于它们是对应的并且具有相同的目标范围,因此也满足了项目符号1或项目符号3。所以#1
和#2
是同一个实体。但是,它们违反了以下规则:
对于实体 E的任何两个声明:
- 如果一个人将 E 声明为变量或函数,则另一个人应将 E 声明为同一类型之一。
所以,不管如何理解“不同的实体”,第一个例子总是格式不正确的。这些编译器确实给出了正确的诊断。
然而,考虑第二个例子
#include <iostream>
struct D{
int m;
};
auto [x] = D{0}; //#1
void show(){
std::cout<<&x<<"\n";
}
int main(){
extern int x; //#2
std::cout<<&x<<"\n";
}
同样,如果“不同的实体”是前一种意见。然后#1
是结构化绑定,#2
而是变量。根据这个规则:
如果声明位于块作用域 S 中并声明了一个函数 ([dcl.fct]) 或使用 extern 说明符,则该声明不应附加到命名模块 ([module.unit]);它的目标范围是最里面的封闭命名空间范围,但名称绑定在 S 中。
因此,这样的两个声明具有相同的目标范围。因此,它们应该有潜在的冲突。如果“不同的实体”由 [basic.link] 确定。根据 [basic.link#8],它们是同一个实体。不过也违反了
如果一个人将 E 声明为变量或函数,则另一个人应将 E 声明为同一类型之一。
所以,无论如何,第二个例子应该是格式错误的。为什么GCC
给出这样两个实体具有相同地址的结果。相反,Clang
只给出一个链接错误?
另外,是否需要为[basic.link#8]添加一个前置条件,即两个实体首先应该具有相同的种类?