C++中::运算符的规则

11

我认为::运算符可以在访问全局作用域的情况下是一元的。在所有其他情况下,::被视为二元运算符,并且在 N1::N2::N3::n 的情况下,:: 运算符的评估规则等效于以下规则:

((N1::N2)::N3)::n // Error: ::n has not been declared

但是那行代码无法编译。这非常奇怪。标准中没有关于nested-name-specifier评估的任何信息。如果nested-name-specified的评估等同于qualified/unqualified-id,那将是自然的,但标准中没有任何地方说明这一点。所以我们能否假设nested-name-specifier的评估是依赖于实现的?


3
标准在定义“限定符号(qualified-id)”下方明确了“嵌套命名指示符(nested-name-specifier)”,而圆括号在该定义中没有涉及。 - Sneftel
@Sneftel 你说得对。但是评估规则呢? - user2953119
不,我正在寻找后续操作以确定由嵌套名称限定符表示的实体是什么? - user2953119
3
没有评估。作用域解析运算符没有优先级和结合性。表达式a::(b::c)(a::b)::c相对无意义,a::(b + c)也是如此。 - ach
2
据我所知,((N1::N2)::N3)::n 总是一个语法错误,但例如 (N1::N2)::n 如果存在类型 N1::N2 和全局变量 n,并且 n 可以转换为 N1::N2,那么它实际上可以编译通过!这只是意味着与 N1::N2::n 完全不同的事情。 :) - JohannesD
显示剩余2条评论
3个回答

9
[expr.prim.general] (5.1.1/8-9) 中列出了 :: 的解析规则。它要求后面跟着命名空间/类的名称或命名空间/类成员的名称。不允许使用 ()
更具体地,在 [over.oper] (13.5/1) 中,标准定义了 operator-function-idoperator 作为以下操作符之一: new, delete, +, -, !, =, ˆ=, &=, <=, >=, (), [], new[], *, <, |=, &&, delete[], /, >, <<, ||, %, +=, >>, ++, ˆ, -=, >>=, --, &, *=, <<=, ,, |, /=, ==, ->*, , %=, !=, ->..*::?: 在第9条款中被命名为例外,不作为普通的一元或二元操作符。

如果 :: 左侧表示一个类,则右侧是该类的成员,而不是命名空间的成员。 - ecatmur

8
作用域解析运算符::是右关联的,这是因为嵌套名称指定符的递归语法是右关联的:
nested-name-specifier:
    ::[opt] type-name ::
    ::[opt] namespace-name ::
    decltype-specifier ::
    nested-name-specifier identifier ::
    nested-name-specifier template[opt] simple-template-id ::

递归评估嵌套名称限定符的适当规则是3.4.3 [basic.lookup.qual]:
1-可将类或命名空间成员或枚举器的名称在双冒号作用域解析运算符(5.1)应用于表示其类、命名空间或枚举器的嵌套名称限定符之后引用。[...]
重要的是,与5.1不同,后者仅讨论限定ID的查找(在5.1.1p8中),3.4.3p1没有限制,因此可以用于递归查找嵌套名称限定符。
也就是说,在以下代码中:
namespace A {
    namespace B {
        struct C {
            struct D {
                static int i;
            };
        };
    }
}
A::B::C::D::i;

A::B::C::D::i被解析为一个限定名(qualified-id),其中包含递归的嵌套名字指示符(nested-name-specifier) A::B::C::D::A::B::C::A::B::A::。现在,要评估A::B::C::D::i

  • 我们使用5.1.1p8,需要评估A::B::C::D
  • 我们使用3.4.3p1,需要评估A::B::C
  • 我们使用3.4.3p1,需要评估A::B
  • 我们使用3.4.3p1,需要评估A

现在我们必须找到一个A“表示”一个“类、命名空间或枚举”的意义。在没有更具体的内容的情况下,第3节将我们转到3.4节:

1- 名称查找规则统一适用于所有名称(包括typedef-name(7.1.3)、namespace-name(7.3)和class-name(9.1)),无论语法规则允许在特定规则讨论的上下文中使用这些名称。[...]

A的查找现在按照3.4.1 [basic.lookup.unqual]的规则在特定上下文中进行。由于我们在全局作用域中,应用3.4.1p4,我们在全局作用域中搜索名称A。我们找到命名空间A,并相应地继续评估A::B::C::D::i


也就是说,嵌套名字指示符(nested-name-specifier)中的最左边(内部)名称被视为未限定名称进行查找;���下来的名称将根据其内部的嵌套名字指示符(nested-name-specifier)作为限定名称进行查找。


1

没有任何实现依赖。你错误地认为它是一个运算符,但它不是。因此,它既不是一元运算符也不是二元运算符,也不形成表达式。由于它不形成表达式,因此这些表达式不会被评估。


7
并非完全如此。标准将::称为作用域解析运算符 - ach
2
@DmitryFucintv,这取决于您如何定义术语。标准确实称其为运算符。 - ach
1
它是一个运算符 - 但是在编译时解析的运算符。 - Rob
2
@MSalters,感谢您的输入,但这是一个有争议的观点。C++'03的确切引用(我目前无法获得C++'11,但我怀疑在这个问题上它不会有所不同)是:“以下运算符不能被重载:. .* :: ?:”。也就是说,从标准的角度来看,这四个都是运算符,尽管具有一些特殊功能:在这种情况下,涉及到运算符重载。 - ach
1
@AndreyChernyakhovskiy:第9条规定:“未在子条款13.5.3至13.5.7中明确提及的运算符作为遵守13.5.1或13.5.2规则的普通一元和二元运算符。”此外,在第1条中,它将operator定义为我提到的列表,意思是它们模仿函数。关于运算符的其他部分都涉及到这个部分,因此不仅仅是关于重载的问题。 - Danvil
显示剩余10条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接