Видимость псевдонима шаблона во вложенном классе

Учтите следующее:

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)

1 ответ

Решение

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


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

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

И [basic.lookup] / 3:

Injected-class-name класса (раздел 9) также считается членом этого класса в целях поиска name […].

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

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

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

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

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

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


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

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

Другие вопросы по тегам