Должно ли имя, найденное в зависимом базовом классе, скрыть имя пространства имен в момент создания экземпляра?

В следующем коде 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>::N при поиске N "в контексте всего постфиксного выражения" и "в точке определения шаблона".

Цитата из С++ Рабочий проект стандарта N3242 = 11-0012 (февраль 2011):

3.4.5 Доступ к члену класса [basic.lookup.classref]

Если id-выражение в доступе члена класса является квалифицированным идентификатором формы

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

имя класса или имя пространства имен, следующих за оператором . или ->, рассматривается как в контексте целое postfix-expression и в объем класса выражения объекта. Если имя найдено только в область действия класса выражения объекта, имя должно ссылаться на имя класса. Если имя найдено только в контексте всего постфиксного выражения, имя должно ссылаться на имя класса или имя пространства имен. Если имя найдено в обоих контекстах, имя класса или имя пространства имен должно относиться к тому же юридическое лицо.

14.6.4 Зависимое разрешение имени [temp.dep.res]

При разрешении зависимых имен учитываются имена из следующих источников:

- Объявления, которые видны в точке определения шаблона.

- объявления из пространств имен, связанных с типами аргументов функции как из контекст контекста (14.6.4.1) и контекст определения.

Ответ 1

Это то, что было изменено в С++ 11. Текст, который вы цитируете от С++ 03; до С++ 11 это было неоднозначно, поскольку оба поиски были использованы, и это была ошибка, если они нашли разные имена. В С++ 11 текст:

Если id-выражение в доступе к члену класса является идентификатором-идентификатором формы class-name-or-namespace-name::... class-name-or-namespace-name, следующее за. или → оператор сначала посмотрел в классе выражения объекта и имя, если найдено, используется. В противном случае он контекст всего постфиксного выражения. [Примечание: см. 3.4.3, который описывает поиск имени до::, который будет только найдите имя типа или пространства имен. -end note]

В принципе, это обеспечивает поиск в области видимости класса и не делает другого, если имя найдено.

Что касается этого, это повлияло только на шаблон в старых версиях стандарта: я думаю (трудно быть уверенным во всем здесь), что это связано с тем, что в случае не-шаблона поиск в контексте всего постфиксного выражения также находит typedef в базовом классе, поэтому оба решения поиска к одному и тому же объекту. В случае шаблона поиск в контекст всего постфиксного выражения не принимает учитывайте зависимый базовый класс и только находит пространство имен N. Однако после инстанцирования C поиск в область действия класса находит typedef. Поскольку два поиска найти разные объекты, привязка имени неоднозначна.