c++ 11中单个限定名和两个连续限定名之间的歧义
C++11 ambiguity between single qualified name and two consecutive?
下面的c++ 11程序是病态的吗?
struct a
{
struct b { };
void f() {};
};
extern struct a b;
struct a ::b;
int main()
{
b.f();
}
为什么/为什么不?
这里有趣的是这一行:
struct a ::b;
是内部类a::b
的前向声明吗?
或者这是全局变量b
的定义?相当于:
struct a (::b);
struct a ::b;
没有声明类型为a
的名为b
的变量,如果这就是您要问的。这是嵌套类型a::b
的(冗余)前向声明。在c++程序中,空格通常不重要。因此,程序声明了一个名为b
的变量,但从未定义它。这违反了"一个定义规则":程序因此是病态的,链接器会告诉你这些。
第2张
struct a ::b;
是struct a::b
的格式良好的声明。
它不是forward声明,因为struct a::b
已经是定义的。标准并没有正式定义前向声明,但是它被普遍接受的意思是在和之前声明某事吗是与其定义分开的。
也不是全局变量b
的声明。
空格是不重要的,所以为了使假定的声明有建议的歧义,
/*A*/ struct a::b;
当然也必须具有同样的模糊性。像这样重写,我们可以我们同意宣言的含义是非常明显的;但是我们我想知道这个明显的意思是否被《标准》所证实。
我将把我的考虑局限于c++ 11标准。我想是一个决议c++ 03标准中这个问题的答案实际上更直接)。
标准将一元作用域操作符::
与作用域区分开来解析算子::
.
§3.4.3限定名查找第1段:
类或命名空间成员或枚举数的名称可以在::作用域解析之后引用操作符(5.1)应用于嵌套名称说明符,表示其类、名称空间或枚举。
§3.4.3限定名查找,第4段:
以一元作用域操作符::(5.1)为前缀的名称在翻译单元的全局作用域中查找
这些引用不是为了解释的区别,只是为了表明这一点它的存在。但他们马上传达了/*A*/
的观点对于§3.4.3/4,操作符必须是一元作用域操作符维持/*A*/
的全球::b
解释。
/*A*/
中的作用域操作符是而不是一元操作符。但是我们提到了§5.1主表达式为一元范围操作符和范围的语法定义解析运算符,在任何情况下,我们都还没有看到关于当if是作用域的右操作数时,查找b
解析操作符.在§5.1.1/8,我们发现运算符::
被定义为§3.4.3/1的中缀运算符,也可作为的一元运算符§3.4.3/4在qualified-id:
qualified-id:
nested-name-specifier template[opt] unqualified-id
:: identifier
:: operator-function-id
:: literal-operator-id
:: template-id
nested-name-specifier:
::[opt] type-name ::
::[opt] namespace-name ::
decltype-specifier ::
nested-name-specifier identifier ::
nested-name-specifier template[opt] simple-template-id ::
当它是a的最后一个记号时,它是中缀操作符嵌套名称说明符后面可选地跟着template
和然后是非限定id,否则是中的一元操作符背景:
:: identifier
:: operator-function-id
:: literal-operator-id
:: template-id
根据这个语法,::
运算符必须左关联a嵌套名称说明符(形成范围解析操作符)如果它可以。根据语法,/*A*/
中的a::
是嵌套名称说明符,a::b
是第一种形式的限定idnested-name-specifier template[opt] unqualified-id.
所以我们确信§3.4.3限定名查找适用于b
in/*A*/
,可参考§3.4.3.1类成员,第1段;在该上下文中,b
应该在a
中查找,而不是全局查找:
如果限定id的嵌套名称说明符指定了一个类,则在嵌套名称后面指定的名称指定符在类(10.2)的作用域中查找,下面列出的情况除外。
"下列情况"均不适用于/*A*/
,如果有任何疑问嵌套类a::b
计数为a
的成员,§9.2类成员,第1段,删除它:
类的成员包括数据成员、成员函数(9.3)、嵌套类型和计数器。
发现a::b
明确要求b
在a
中查找,/*A*/
的良构性问题变成了是否/*A*/
是一个声明标准(当然struct a;
在相同的
是的,我们可以通过几个步骤来确保这一点声明语法:-
Per§7声明,第1段,声明可以是简单声明,简单声明可以是decl-specifier-seq。
Per§7.1说明,第1段,adecl-specifier-seq可以是decl-specifierdecl-specifier可以是类型说明符。
Per§7.1.6类型说明符在第1段中,类型说明符可以是elaborated-type-specifier.
Per§7.1.6.3详细的类型说明符,第0段,详细类型说明符可以是:
class-key attribute-specifier-seq[opt] nested-name-specifier[opt] identifier
whereclass-key是class
,struct
或union
(per§9Classes),第1段)而可选的属性指定符-seq并不重要,因为我们不需要需要它。
/*A*/
满足:
class-key nested-name-specifier identifier
所以它是一个声明。
Per Standard,/*A*/
是a::b
的重新声明。
关于GCC和CLANG的一些评论
Clang 3.3诊断发布的代码:
error: forward declaration of struct cannot have a nested name specifier
这是错误的,因为声明不是向前的。如果我们转移犯罪clang声明,使得在a::b
的定义之前,而不是错误格式错误的前向声明,而是诊断为:
error: use of undeclared identifier 'a'
如果将程序中的struct a ::b;
替换为struct a ::b x;
,则Clang认为这句话没有错,因此他找到了一个定义a::b
在先前它持有该类型为(非法的)的同一点forward-declared .
GCC 4.8.1诊断:
warning: declaration ‘struct a::b’ does not declare anything
undefined reference to `b'
警告只是一个警告,与声明是一致的是一个格式良好的重新声明,但可能会更有帮助词"新"。如果我们移动冒犯的声明,使它实际上是向前的,那么GCC,自然会给出与clang相同的错误。
第二个链接错误是指未定义的extern struct b
在main
中调用,并且正确地提供了而不是有struct a ::b;
的定义
- 两个连续的 OpenMP 并行区域会相互减慢速度
- 可以将两个相同类型的连续数组视为一个数组吗?
- 我已经建立了递归关系,它找到了两个字符串之间最长的连续公共字符串,我怎么能跳过其中一个字符串中的一个字符
- 比较两个std::矢量/阵列,或者通常比较两个stl连续器
- 使用 C++ 读取具有两个连续分隔符的 csv 文件
- 为什么动态分配的两个变量的内存位置不是连续的?
- 子数组中两个数字的相同出现(连续)
- 如何比较C 列表中的两个连续元素
- 为什么两个执行矩阵乘法的过程并行运行比连续运行慢
- 如何生成没有两个相邻连续数字的排列
- 两个最大的连续子阵列
- 是否有任何方法可以比较C 中的STD ::的两个连续元素
- 为什么我不能在同一分配中保留两个连续的内存区域,而不通过单个调用保留这两个区域?
- 如何比较两个连续的迭代步骤?在C++
- 为什么两个连续的转换会改变一个整数
- 对于同一循环的这两个连续组,是否有更有效的解决方案
- Boost.SSpirit.x3避免将同一类型的两个连续属性塌陷为向量
- 如何检测两个连续的视频帧是否相同
- 在整数数据类型和连续两个字符类型之后.第 2 个字符的数据类型跳过..为什么
- 连续两个"cin"不起作用