当实例化一个模板时,它的不完全的参数类型的成员应该是可见的吗? [英] When instantiating a template, should members of its incomplete argument types be visible?
问题描述
在以下示例中, 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
:实例化,所以编译器的行为是有意义的 - 但我还没有看到任何明确描述这种行为的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
。在非模板的情况下,这是不成立的(试图忘记一会儿,你正在处理模板:肯定的查找 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屋!