Инъекционные имена шаблонов классов

Вдохновленный кодом в этом ответе. Рассмотрим:

template<class>
class A { };

int main()
{
    A<float> a(A<float>::A<int>());
    return 0;
}

Этот код

  • плохо сформирован, потому что A<float>::A называет конструктор (в §3.4.3.1 [class.qual]/p2) и не может быть использован в этом контексте (плюс <int> завершается, чтобы не разбираться в любом случае), или
  • корректно, при этом A<float>::A является именем введенного класса, используемым в качестве имени шаблона (§14.6.1 [temp.local]), так что A<float>::A<int> означает точно такое же, как A<int>, и a объявляется как функция (из-за самого досадного разбора)?

g++ говорит 1. clang говорит 2, и поэтому ICC 13. Какой компилятор прав?

Ответ 1

gcc правильный; ваш фрагмент плохо сформирован!

// reduced testcase
template<class T>
class A { };

int main () {
  A<float>::A<int> x; // ill-formed, bug in `clang` and `icc`
}

В приведенном выше тестовом сценарии мы имеем спецификатор вложенных имен, A<float>::, за которым следует неквалифицированный идентификатор A, за которым следует некоторая тарабарщина (<int>).

Это связано с тем, что контекст, в котором появляется спецификатор вложенных имен, указывает, что во время поиска включаются имена функций (это означает, что сначала найден конструктор, а выражение плохо сформировано).


Релевантные отчеты об ошибках:


Как обойти "проблему"?

Существуют контексты, в которых имена участников, которые просматриваются через спецификатор вложенных имен (который назначает класс), не должны включать функции (следовательно, контексты, в которых конструктор не найден), ниже приведены несколько примеров:

template<class T>
struct A {
  typedef T value_type;
};
  struct A<float>::A<int>  x;     // ok, context: elaborate-type-specifier
typename A<float>::A<int> ();     // ok, context: [expr.type.conv]p1
  A<float>::A::value_type  x;     // ok, context: nested-name-specifier


struct X : A<float>::A<int> { };  // ok, context: base-specifier

Что говорит стандарт?

3.4.3.1p2 Члены класса [class.qual]

В поиске, в котором имена функций не игнорируются 88 а спецификатор вложенных имен назначает класс C:

     
  •   
  • если имя, указанное после вложенного имени-спецификатора, при поиске на C, представляет собой введенное имя класса C (раздел 9) или  
  • в декларации using (7.3.3), которая является объявлением-членом, если имя, указанное после спецификатора вложенных имен, совпадает с идентификатором или именем шаблона-шаблона в последнем компонент * вложенного имени-спецификатора,  
     

вместо этого имя считается именем конструктора класса C.

     

[Примечание:...]

     

Такое имя конструктора должно использоваться только в объявлении-id объявления, которое называет конструктор или декларацию использования.

     
     

88. Поиск, в котором игнорируются имена функций, включают имена, появляющиеся в вложенном имени-спецификаторе, специфицированный тип-спецификатор или базовый-спецификатор.

14.6.1p2 Локально объявленные имена [temp.local]

Подобно обычным (не шаблонным) классам, шаблоны классов имеют имя с введенным классом   (Пункт 9). Имя введенного класса может использоваться как имя шаблона или   Имя-типа.

     

Когда он используется с шаблоном-аргументом-списком, как   шаблон-аргумент для шаблона-шаблона шаблона или как окончательный   идентификатор в специфицированном спецификаторе типа шаблона класса друга   декларации, это относится к самому шаблону класса.

     

В противном случае, это   эквивалентно имени шаблона, за которым следуют шаблонные параметры   шаблон класса, заключенный в <>.