我有以下C ++代码:
//Define to 1 to make it work
#define WORKS 0
#if WORKS
template< typename T > struct Foo;
#else
template< typename T >
struct Foo {
T t;
};
#endif
class Bar; //Incomplete type
void fFooBar(Foo<Bar> const & foobar) { }
void f(Foo<Bar> const & foobar) {
fFooBar(foobar);
}
int main() {
return 0;
}
如果将WORKS定义为0(定义了struct模板),则代码将无法编译,因为它尝试将其实例化fFooBar(foobar);
并失败,因为代码Bar
不完整。
如果将WORKS定义为1(未定义结构模板),则代码将编译。
根据该标准,除非要求使用完整的类型(对于而言并非如此const&
),否则模板不应被实例化,否则模板将改变代码的语义(同样,情况并非如此,再次如此)如果模板未定义,则会发生)。
另外,很奇怪,可以通过从编译单元中删除信息来使程序进行编译。但是,MSVC,gcc和clang都执行相同的事实,这使我认为背后一定有原因。
当WORKS=0
,可以使程序在锵由呼叫排位编译fFooBar
使用::
。该标准要求在函数调用中使用非限定名称时,名称查找的行为有所不同。
[basic.lookup.argdep] / 1
当函数调用(5.2.2)中的postfix-expression是非限定id时,可以搜索在通常的非限定查找(3.4.1)中未考虑的其他命名空间,并在这些命名空间中,命名空间范围的朋友函数声明( 11.3)可能无法找到。
检查“自变量依赖查找”过程的(有些复杂)规则表明,它只能以需要实例化调用调用的自变量类型的模板专门化的方式正确实现。
[basic.lookup.argdep] / 2
对于函数调用中的每个参数类型T,要考虑一组零个或多个关联的名称空间和一组零个或多个关联的类。名称空间和类的集合完全取决于函数参数的类型[...]
- 如果T是一个类类型(包括并集),则其关联的类为:类本身;它所属的类(如果有);及其直接和间接基类。
对此的一种解释是,如果将一个类用于不合格函数调用的参数类型,则要求该类是完整的。另一种解释是ADL应该只导致实例化完整的模板。
两种行为均符合工作草案N3337的标准
[temp.inst] / 6
如果重载解析过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则不确定该实例化是否真正发生。
template <class T> struct S {
operator int();
};
void f(int);
void f(S<int>&);
void f(S<float>);
void g(S<int>& sr) {
f(sr); // instantiation of S<int> allowed but not required
// instantiation of S<float> allowed but not required
};
[temp.inst] / 7
如果需要隐式实例化类模板的特殊化,并且声明但未定义模板,则程序格式错误。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句