实例化模板时,其不完整参数类型的成员是否应该可见?

wil*_*llj 6 c++ templates language-lawyer

在下面的示例中,A有一个成员typedef Instantiate导致实例化B<A>.

template<typename T>
struct B
{
    typedef typename T::Before Before; // ok
    typedef typename T::After After; // error: no type named 'After' in 'A<int>'
};

template<typename T>
struct A
{
    typedef int Before;
    typedef typename B<A>::After Instantiate;
    typedef int After;
};

template struct A<int>; // instantiate A<int>
Run Code Online (Sandbox Code Playgroud)

我尝试过的所有编译器都报告说,虽然A::Before可见但A::After并非如此.这种行为是否符合标准?如果是这样,标准在哪里指定A在实例化期间哪些名称应该可见B<A>

如果依赖名称"在模板实例化时查找",那么在由模板参数限定的名称场景中,这意味着什么T::After

编辑:请注意,当A不是模板时,会发生相同的行为:

template<typename T>
struct B
{
    typedef typename T::Before Before; // ok
    typedef typename T::After After; // error: no type named 'After' in 'A'
};

struct A
{
    typedef int Before;
    typedef B<A>::After Instantiate;
    typedef int After;
};
Run Code Online (Sandbox Code Playgroud)

..和G ++接受以下内容,但Clang没有:

template<typename T>
struct B
{
    static const int value = 0;
    static const int i = T::value; // clang error: not a constant expression
};

struct A
{
    static const int value = B<A>::value;
};
Run Code Online (Sandbox Code Playgroud)

编辑:经过一些C++ 03标准的阅读:

[temp.dep.type]如果类型是模板参数,则该类型是相关的

因此T是依赖的.

[temp.res]在查找模板定义中使用的名称声明时,通常的查找规则用于非依赖名称.依赖于模板参数的名称查找被推迟,直到知道实际模板参数.

因此,查询T::After被推迟,直到T知道参数为止.

[temp.inst]除非已显式实例化类模板特化,否则在需要完全定义的对象类型的上下文中引用特化时,将隐式实例化类模板特化.

因此声明A<int>::Instantiate需要实例化B<A>(因为它在嵌套名称说明符中使用.)

A<int>::After在声明时是不可见的A<int>::Instantiate,因此编译器的行为是有意义的 - 但我没有在C++ 03中看到任何明确描述此行为的内容.最接近的是这个有点模糊的段落:

[temp.dep.res]在解析依赖名称时,会考虑以下来源的名称:

- 在模板定义点可见的声明.

Joh*_*itb 4

typename T::Before规范中没有明确说明是否有效。它是缺陷报告的主题(因为可以非常合理地阅读该标准来禁止它):http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#287

是否typename T::After无效也可以很合理地被规范解读为正确,实际上它很有道理(前面提到的 DR 仍然保持它的格式错误)。因为您有一个类的实例化A<Foo>,它在尚未声明A<Bar>成员的期间引用了另一个类,并且这会引用回. 在非模板的情况下,这也是格式不正确的(尝试暂时“忘记”您正在处理模板:当然,查找是在模板完全解析之后完成的,而不是在它的特定实例化之后完成的已完全创建。并且它的实例化实际上将进行引用!)。BazA<Foo>::BarB<A>::AfterA

struct A {
   typedef int Foo;
   typedef A::Foo Bar; // valid
   typedef A::Baz Lulz; // *not* valid
   typedef int Baz; 
};
Run Code Online (Sandbox Code Playgroud)