当实例化一个模板时,它的不完全的参数类型的成员应该是可见的吗? [英] When instantiating a template, should members of its incomplete argument types be visible?

查看:109
本文介绍了当实例化一个模板时,它的不完全的参数类型的成员应该是可见的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在以下示例中, A 有一个成员typedef 实例化,这会导致 B

 模板< typename T> 
struct B
{
typedef typename T :: Before Before; // ok
typedef typename T :: After After; //错误:在'A< int>'
}中没有名为After的类型

template< typename T>
struct A
{
typedef int Before;
typedef typename B< A> ::实例化之后;
typedef int After;
};

template struct A< int> ;; // instantiate A< int>

我试过的所有编译器报告, A :: Before 是可见的, A ::之后不是。这种行为是否符合标准?如果是,标准在 A 中的哪些名称应该在 B / p>

如果依赖名称是在模板实例化时查找,那么在由模板参数限定的名称的情况下,这意味着什么?例如 T :: After



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

  template< typename T> 
struct B
{
typedef typename T :: Before Before; // ok
typedef typename T :: After After; //错误:没有类型命名为'After'in'A'
};

struct A
{
typedef int Before;
typedef B< A> ::实例化之后;
typedef int After;
};

..和G ++接受以下内容,但Clang不能:

 模板< 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;
};

编辑:在阅读了C ++ 03标准之后:


[temp.dep.type]如果它是模板参数,则类型是依赖


因此 T 是依赖的。


<查找模板定义中使用的名称的声明,通常的查找规则用于非独立名称。依赖于模板参数的名称的查找被推迟,直到实际的模板参数被知道。


查找 T :: After 因此被推迟,直到 T 的参数是已知的。


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


因此,声明 A< int& ::实例化需要实例化 B< A> (因为它用于嵌套名称说明符。)



A A< int> ;:声明时不能看到 :实例化,所以编译器的行为是有意义的 - 但我还没有看到任何明确描述这种行为的C ++ 03。最接近的是这个有些模糊的段落:


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



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



解决方案

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



是否 typename T :: After 无效也可以非常合理地被规范读取为true,实际上它有点有意义(和上述DR仍然保持不良形式)。因为您有一个类 A 的实例化,它在一段时间内引用了另一个类 A< Bar> 其中成员 Baz 尚未被声明,并且返回 A :: Bar 。在非模板的情况下,这是不成立的(试图忘记一会儿,你正在处理模板:肯定的查找 B< A> :: After 在之后完成之后, A 模板完全解析完毕,但

  struct A {
typedef int Foo;
typedef A :: Foo Bar; // valid
typedef A :: Baz Lulz; // * not * valid
typedef int Baz;
};


In the following example, A has a member typedef Instantiate which causes the instantiation of 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>

All the compilers I've tried report that, while A::Before is visible, A::After is not. Is this behaviour compliant with the standard? If so, where does the standard specify which names in A should be visible during instantiation of B<A>?

If dependent names are "looked up at the point of the template instantiation", what does this mean in the scenario of a name qualified by a template parameter such as T::After?

EDIT: Note that the same behaviour occurs when A is not a template:

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;
};

.. and G++ accepts the following, but Clang does not:

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;
};

EDIT: After some reading of the C++03 standard:

[temp.dep.type] A type is dependent if it is a template parameter

Therefore T is dependent.

[temp.res] When looking for the declaration of a name used in a template definition, the usual lookup rules are used for nondependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known.

The lookup of T::After is therefore postponed until the argument for T is known.

[temp.inst] Unless a class template specialization has been explicitly instantiated ... the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type.

Therefore the declaration of A<int>::Instantiate requires the instantiation of B<A> (because it is used in a nested-name-specifier.)

A<int>::After is not visible at the point of declaration of A<int>::Instantiate, so the behaviour of the compiler makes sense - but I haven't seen anything in C++03 that explicitly describes this behaviour. The closest thing was this somewhat vague paragraph:

[temp.dep.res] In resolving dependent names, names from the following sources are considered:

— Declarations that are visible at the point of definition of the template.

解决方案

Whether typename T::Before is valid is not explicitly said by the spec. It is subject of a defect report (because the Standard can very reasonably be read to forbid it): http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#287 .

Whether typename T::After is invalid can also very reasonably be read to be true by the spec, and actually it makes quite a bit of sense (and aforementioned DR still keeps it ill-formed). Because you have an instantiation of a class A<Foo>, which references another class A<Bar> during a period where a member Baz has not yet been declared, and that makes a reference back to A<Foo>::Bar. That is ill-formed in the case of non-templates aswell (try to "forget" for a moment that you are dealing with templates: surely the lookup of B<A>::After is done after the A template was completely parsed, but not after the specific instantiation of it was completely created. And it is the instantiation of it that actually will do the reference!).

struct A {
   typedef int Foo;
   typedef A::Foo Bar; // valid
   typedef A::Baz Lulz; // *not* valid
   typedef int Baz; 
};

这篇关于当实例化一个模板时,它的不完全的参数类型的成员应该是可见的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆