Should a class-member using-declaration with a dependent qualified-id be a dependent name?

2.4k views Asked by At

Draft N3337 of the C++11 standard states in [namespace.udecl]

A using-declaration introduces a name into the declarative region in which the using-declaration appears.

Every using-declaration is a declaration and a member-declaration and so can be used in a class definition.

In a using-declaration used as a member-declaration, the nested-name-specifier shall name a base class of the class being defined.

This is generally used to make a protected typedef within a base-class public in the derived class, as in the following example, which compiles successfully in the latest version of Clang:

struct A
{
protected:
    typedef int Type;
};

struct B : A
{
    using A::Type;
};

B::Type x;

The using-declaration can refer to a template class. This compiles:

struct A
{
protected:
    template<typename T>
    struct Type
    {
    };
};

struct B : A
{
    using A::Type;
};

B::Type<int> x;

It's also possible to refer to a template in a dependent base-class. The following compiles successfully (with the typedef commented.)

template<typename T>
struct A
{
protected:
    template<typename U>
    struct Type
    {
    };
};


template<typename T>
struct B : A<T>
{
    using /* typename */ A<T>::Type; // A<T> is dependent, typename required?
    // typedef Type<int> IntType; // error: unknown type name 'Type'
};

B<int>::Type<int> x;

Uncommenting the typename causes an error when instantiating B<int>: "error: 'typename' keyword used on a non-type".

Uncommenting the typedef causes an error when parsing B before its first instantiation. I'm guessing this is because the compiler does not treat Type as a dependent type-name.

The last paragraph of [namespace.udecl] suggests that using-declarations may specify dependent names, and that the typename keyword must be used in order to disambiguate further usage of the name introduced:

If a using-declaration uses the keyword typename and specifies a dependent name (14.6.2), the name introduced by the using-declaration is treated as a typedef-name

My reading of [temp.dep] suggests that A<T>::Type is a dependent name. It follows logically that the name introduced by the using-declaration should also be dependent, but [temp.dep] does not explicitly mention the case of a dependent using-declaration. Am I missing something?

2

There are 2 answers

3
Anton Savin On

The problem is that Type is not a class, but a class template. You can do the following (this way you tell the compiler that Type is a class template in scope of B):

template<typename T>
struct B : A<T>
{
    using A<T>::Type;
    typedef typename B::template Type<int> IntType;
};

Actually, in your second example in order to write typedef for IntType you'd have to do the same.

0
willj On

Yes, a class-member using-declaration with a dependent qualified-id introduces a dependent name.

[namespace.udecl]

A using-declaration introduces a name into the declarative region in which the using-declaration appears.

If the name introduced is dependent, it stays dependent - I can find nothing to suggest otherwise.

However, the using-declaration syntax does not provide a way to specify that a dependent name is a template. Subsequent references to the dependent name Type within B may or may not refer to a template, so Type<int> cannot be parsed.

The following example demonstrates valid usage of a dependent using-declaration.

template<typename T>
struct A
{
protected:
    struct Type
    {
        typedef int M;
    };
};


template<typename T>
struct B : A<T>
{
    using typename A<T>::Type;
    typedef typename Type::M Type2;
};

B<int>::Type2 x;