Должно ли имя, найденное в зависимом базовом классе, скрыть имя пространства имен в момент создания экземпляра?
В следующем коде 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
. Поскольку два поиска
найти разные объекты, привязка имени неоднозначна.