10

使用以下代码,clang 3.0 给出error: lookup of 'N' in member access expression is ambiguous,而 clang 3.4 和 gcc 4.8 都接受代码而没有错误。

struct B
{
    struct M
    {
        void f()
        {
        }
    };
};

namespace N
{
    struct M
    {
        void f()
        {
        }
    };
}

template<typename>
struct A : N::M, B::M
{
    typedef B N;
};

struct D : A<int>
{
    A<int> m;
    void f()
    {
        m.N::M::f(); // found class-name 'A<int>::N' (unambiguous)
    }
};

template<typename T>
struct C : A<T>
{
    A<T> m;
    void f()
    {
        m.N::M::f(); // found namespace-name 'N' (ambiguous?)
    }
};

template struct C<int>;

在查阅标准后,我不清楚关于C<T>::f().

因为N在对象表达式的类范围n(依赖)和整个后缀表达式的上下文(即函数的范围C<T>::f())中查找,所以需要延迟查找直到实例化点.

N在实例化时,如果同时找到命名空间和 typedef ,查找将是不明确的A<T>::N。的声明N只有在没有被 的声明隐藏时才可见A<T>::N

问题是在“在整个后缀表达式的上下文中”和“在模板的定义点”查找时,名称空间是否N应该被认为被 typedef 隐藏。A<T>::NN

引自 C++ 工作草案标准 N3242=11-0012(2011 年 2 月):

3.4.5 类成员访问 [basic.lookup.classref]

如果类成员访问中的 id-expression 是以下形式的限定 id

class-name-or-namespace-name::...

.or运算符后面的类名或命名空间名在整个后缀表达式的上下文和对象表达式的类的范围内->都被查找。如果名称仅在对象表达式的类范围内找到,则该名称应指类名。如果该名称仅在整个后缀表达式的上下文中找到,则该名称应指类名或命名空间名。如果在两个上下文中都找到该名称,则类名或命名空间名应指同一实体。

14.6.4 从属名称解析 [temp.dep.res]

在解析从属名称时,会考虑来自以下来源的名称:

在模板定义时可见的声明。

— 来自实例化上下文 (14.6.4.1) 和定义上下文中与函数参数类型相关联的命名空间的声明。

4

1 回答 1

5

这在 C++11 中有所改变。你引用的文字来自C++03;在 C++11 之前,这是模棱两可的,因为两个查找都被使用了,如果他们发现不同的名字就是一个错误。在 C++11 中,文本为:

如果类成员访问中的 id-expression 是 class-name-or-namespace-name::... 形式的限定 id,则 . 或 -> 运算符首先在对象表达式的类中查找,如果找到,则使用名称。否则在整个后缀表达式的上下文中查找它。[ 注意:参见 3.4.3,它描述了在 :: 之前查找名称,它只会查找类型或命名空间名称。——尾注]

基本上,这对类范围内的查找具有特权,如果找到名称,则不会执行另一项。

至于为什么这只影响旧版本标准中的模板:我认为(这里很难确定任何事情)这是因为在非模板的情况下,整个后缀上下文中的查找-expression 还在typedef基类中找到 ,因此两个查找都解析为同一个实体。在模板的情况下,整个后缀表达式上下文中的查找不考虑依赖的基类,而只查找命名空间 N。然而,在 的实例化之后C,在类范围内的查找会找到typedef. 由于两个查找找到不同的实体,名称绑定是不明确的。

于 2013-08-13T11:32:56.060 回答