关于C++:具有依赖qualified-id的类成员using声明是否应该是依赖名称?

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

EDCOX1〔0〕中C++ 11标准状态的N33 37

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.

这通常用于使基类中的受保护typedef在派生类中成为公共的,如下面的示例所示,后者在最新版本的clang中成功编译:

1
2
3
4
5
6
7
8
9
10
11
12
struct A
{
protected:
    typedef int Type;
};

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

B::Type x;

using声明可以引用模板类。编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct A
{
protected:
    template<typename T>
    struct Type
    {
    };
};

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

B::Type<int> x;

也可以引用依赖基类中的模板。以下编译成功(typedef被注释)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;

取消对typename的注释会在实例化B时导致错误:"错误:非类型上使用了‘typename’关键字"。

取消对typedef的注释会在分析B的第一个实例化之前导致错误。我猜这是因为编译器不将Type视为依赖类型名。

[namespace.udecl]的最后一段建议,使用声明可以指定从属名称,并且必须使用typename关键字以消除对引入名称的进一步使用的歧义:

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

我读到的[temp.dep]表明A::Type是一个从属名称。从逻辑上讲,using声明引入的名称也应该是从属的,但是[temp.dep]没有明确提到从属using声明的情况。我错过什么了吗?


问题是Type不是类,而是类模板。可以执行以下操作(这样可以告诉编译器TypeB范围内的类模板):

1
2
3
4
5
6
template<typename T>
struct B : A<T>
{
    using A<T>::Type;
    typedef typename B::template Type<int> IntType;
};

实际上,在您的第二个示例中,为了为IntType编写typedef,您必须这样做。


是的,使用具有从属限定ID的声明的类成员引入了从属名称。

[namespace.udecl]

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

如果引入的名称是依赖的,它将保持依赖性——否则我找不到任何建议。

但是,using声明语法不提供指定依赖名称是模板的方法。在B中对从属名称Type的后续引用可能引用模板,也可能不引用模板,因此无法分析Type

下面的示例演示Dependent Using声明的有效用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;