在从属基类中找到的名称是否应该在实例化时隐藏名称空间名称?

wil*_*llj 10 c++ templates language-lawyer

使用以下代码,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>;
Run Code Online (Sandbox Code Playgroud)

在查阅标准之后,我不清楚哪个行为在表达方面是正确的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是表单的qualified-id

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

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

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

在解析依赖名称时,会考虑以下来源的名称:

- 在模板定义点可见的声明.

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

Jam*_*nze 5

这是在C++ 11中改变的.你引用的文字来自C++ 03; 在C++ 11之前,这是不明确的,因为使用了两个查找,如果他们找到不同的名称则是错误的.在C++ 11中,文本是:

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

基本上,这会在类范围内对查找进行特权,如果找到名称,则不会执行另一个查找.

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