c++中::操作符的规则



我认为在访问全局作用域的情况下,::操作符可以是一元的。在所有其他情况下,将::视为二元算子,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的求值是依赖于实现的吗?

[expr.prim.general](5.1.1/8-9)中列出了::的解析规则。它要求::后跟名称空间/类的名称或名称空间/类的成员的名称。不允许使用()

更确切地说在[over.oper](13.5/1)标准定义了operator-function-idoperator new之一,delete, +, -, !, =, ˆ=, &=, <=, >=, (), [], new[], *, <, |=, &&, delete[], /, >, <<, ||, %, +=, >>, ++, ˆ, -=, >>=, --, &, *=, <<=, ,, |, /=, ==, ->*, , %=, != ->

., .*, ::, ?:在第9条中被命名为不作为普通一元或二元操作符的例外。

范围解析运算符::是右结合的,因为嵌套名称说明符的递归语法是右结合的:

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)应用于表示其类、命名空间或枚举的嵌套名称说明符之后引用。[…]

重要的是,与只讨论查找限定ids(在5.1.1p8中)的5.1不同,3.4.3p1是不受限制的,因此可用于递归查找嵌套名称说明符s。

即:

namespace A {
    namespace B {
        struct C {
            struct D {
                static int i;
            };
        };
    }
}
A::B::C::D::i;

A::B::C::D::i被解析为限定id,递归地包含嵌套名称说明符 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-names (7.1.3), namspace -names(7.3)和class-names(9.1)),只要语法允许在特定规则讨论的上下文中使用这些名称。[…]

A的查找现在根据3.4.1 [basic.lookup]的规则进行。unequal]应用于特定上下文。因为我们在全局作用域中,所以应用3.4.1p4,我们在全局作用域中搜索名称A。我们找到命名空间A,并对A::B::C::D::i求值。


也就是说,嵌套名称说明符中的最左边的(最里面的)名称将作为非限定名称查找;连续的名称在其内部嵌套名称说明符上作为限定名称查找。

没有任何实现依赖。但是,您错误地假设它是一个运算符。它不是。因此,它既不是一元操作符,也不是二元操作符,而且它不形成表达式。因为它不形成表达式,所以没有对这些表达式求值。

最新更新