2015-11-17 2 views
12

Рассмотрим следующий пример:Шаблон видимость псевдонима в вложенного класса

template<typename X> 
struct Z {}; 

struct A 
{ 
    using Z = ::Z<int>; 

    struct B : Z 
    { 
     using C = Z; 
    }; 
}; 

Это нормально компилируется. Ницца. Но теперь добавить еще один параметр в Z:

template<typename X, typename Y> 
struct Z {}; 

struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; // error: too few template arguments for class template 'Z' 
    }; 
}; 

Хорошо, может быть, имеет смысл, что определение шаблона псевдонима Z в классе A виден при выводе вложенного класса B, но не внутри ее тела, вызывая погрешность, поскольку глобальное определение Z имеет два параметра.

Но почему это поведение отличается в первом случае, когда Z просто тип псевдонима в A?

Наконец, сделать A шаблон:

template<typename X, typename Y> 
struct Z {}; 

template<typename T> 
struct A 
{ 
    template<typename X> 
    using Z = ::Z<X, int>; 

    struct B : Z<B> 
    { 
     using C = Z<B>; 
    }; 
}; 

Теперь больше нет ошибки. Почему?

(Проверено на Clang 3.6 и GCC 4.9.2)

ответ

9

Вкратце: Выведение из специализации Z вводит имя-впрыскивают класса из ::Z, которая находится перед шаблоном псевдонима. Если A является шаблоном, однако имя-инъекционное имя больше не найдено, потому что базовый класс B зависит.


Рассмотрим [temp.local]/1:

Как и обычные (не шаблон) классов, шаблоны классов имеют впрыскивается класса имя- (раздел 9). Вводимый класса имя-может быть использован в качестве шаблона -имени в или имя-типа в .

И [basic.lookup]/3:

впрыскивается класса имя- из класса (пункт 9) также считается членом этого класса для целей имени [...] поиск.

Z рассматривается как неквалифицированное название; [Basic.lookup.unqual]/7:

enter image description here

Таким образом, нагнетаемый класса имя-Z найден в качестве члена базового класса Z<B, int>, и используется в качестве шаблона имя- в , что делает вашу вторую программу плохо сформированной.На самом деле, ваш первый фрагмент кода использует имя-впрыскивается класса , а также - следующий фрагмент не компилируется:

struct A 
{ 
    using Z = ::Z<float>; 
    struct B : ::Z<int> 
    { 
     static_assert(std::is_same<Z, ::Z<float>>{}, ""); 
    }; 
}; 

Наконец, если A сделан шаблон, обратите внимание, что B является зависимым типа, per [temp.dep.type]/(9.3) , таким образом Z<B> является зависимым типом согласно [temp.dep.type]/(9.7), поэтому базовый класс Z<B> не рассматривается во время поиска для unqualified-idZ в зависимости от [temp.dep]/3:

В определении класса [..], область действия зависимых базовый класса (14.6.2.1) не рассматриваются во неквалифицированном поиске имени либо в точке определения шаблона класса или элемента или во время создания шаблона или члена класса.

Следовательно, введенное имя класса не будет найдено.


B является "вложенный класс [..], который является членом зависимой текущего экземпляра" (курсив мой), так как

Имя является зависимым член текущего экземпляра, если он является членом текущего экземпляра, который при поиске вверх ссылается на наименее один член класса, который является текущим экземпляром.

+0

Ничего себе. Это совершенно понятно, спасибо. Ошибка на самом деле появилась, когда 'A' ​​перестает быть шаблоном, который, как я думал, упростит код. Однако теперь я вынужден использовать два разных имени для двух 'Z', что делает код более уродливым. Если есть лучший способ обхода проблемы, пожалуйста, дайте мне знать. – iavr

+0

@iavr Как насчет 'использования C = Z;'? (Не будет работать для 'A', являющегося шаблоном) – Columbo

+0

Теперь это впечатляет :-) Да, он работает на этом упрощенном коде здесь, но не на моем оригинальном (« неизвестное имя типа «Z'')». Я должен проверить, где разница. 'A' больше не является шаблоном, и я намерен сохранить его таким образом. – iavr

Смежные вопросы