Я столкнулся с этим странным поведением, проверяя, требуется ли typename
клану. Оба clang и gcc принимают этот код, а msvc отклоняет его.
template<class T1>
struct A
{
template<class T2>
struct B
{
static B f;
static typename A<T2>::template B<T1> g;
};
};
template<class T1>
template<class T2>
typename A<T2>::template B<T1> // ok, typename/template required
A<T1>::B<T2>::g;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename
A<T1>::B<T2>::f;
В общем случае квалифицированный идентификатор A<T1>::B<T2>
(где A<T1>
является зависимым именем) должен быть записан typename A<T1>::template B<T2>
. Является ли поведение gcc/clang некорректным или существует исключение из общего правила (приведенного ниже) в данном конкретном случае?
Можно утверждать, что A<T1>
не является зависимым именем или что B<T2>
относится к члену текущего экземпляра. Однако в момент разбора спецификатора типа невозможно узнать, что текущее создание A<T1>
. Представляется проблематичным требовать, чтобы реализация предполагала, что A<T1>
является текущим экземпляром.
14.6 Разрешение имени [temp.res]
Имя, используемое в объявлении или определении шаблона, и которое зависит от параметра шаблона, предполагается, что не следует указывать тип, если соответствующий поиск имени не найдет имя типа или имя не будет квалифицировано по ключевому слову typename.
14.2 Имена специализированных шаблонов [temp.names]
Когда имя специализации шаблона члена появляется после
.
или->
в постфиксном выражении или после inested-name-specifier в identified-id, а выражение объекта или указателя postfix-expression или inested-name-specifier в identifier-id зависит от параметра шаблона (14.6.2), но не относится к член текущего экземпляра (14.6.2.1), имя шаблона члена должно иметь префикс по ключевому слову шаблон. В противном случае предполагается, что имя называется не-шаблоном.
Чтобы продолжить исследование того, что делает clang, я также пробовал это:
template<class T1>
struct C
{
template<class T2>
struct D
{
static typename A<T1>::template B<T2> f;
static typename A<T1>::template B<T2> g;
};
};
template<class T1>
template<class T2>
typename A<T1>::template B<T2> // ok, typename/template required
C<T1>::D<T2>::f;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang rejects with incorrect error
C<T1>::D<T2>::g;
Clang дает error: redefinition of 'g' with a different type
, но тип g
действительно соответствует объявлению.
Вместо этого я ожидал увидеть диагностику, предполагающую использование typename
или template
.
Это дает основание полагать, что поведение clang в первом примере непреднамеренно.