我试图理解一些C ++语法:
template<class T>
class Foo
{
Foo();
template<class U>
Foo(const Foo<U>& other);
};
template<class T>
Foo<T>::Foo() { /*normal init*/ }
template<class T>
template<class U>
Foo<T>::Foo(const Foo<U>& other) { /*odd copy constructed Foo*/ }
所以,我编写了这样的代码,它恰好在windows和linux中编译。我不明白的是复制构造函数有两个模板定义的原因。基本上,在我找到正确的语法之前,我必须先解释一下,我想知道为什么特定的语法是正确的,而不是template<class T, class U>.
答案 0 :(得分:7)
第一个模板(带参数T)表示类是用参数T模板化的。
第二个模板(带参数U)表示模板化类(带参数T)的成员函数是用参数U模板化的 - 它是构造函数。
实际上,这里有一个模板类,它将生成与用作构造函数参数的类型一样多的复制构造函数。
在复制构造函数的特定情况下,您不应该这样做,而是:
template<class T>
class Foo
{
Foo();
Foo(const Foo<T>& other);
};
template<class T>
Foo<T>::Foo() { /*normal init*/ }
template<class T>
Foo<T>::Foo(const Foo<T>& other) { /*odd copy constructed Foo*/ }
因为在你的例子中,它不是复制构造函数,而是将类型U作为参数的构造函数:转换构造函数......很难预测。
答案 1 :(得分:4)
每个涉及的模板都必须有单独的template
子句。这里涉及两个模板,它们都应该得到它们的(非空)模板子句:
Foo
考虑这个案例因为参数U
所属的位置不明确而失败
template<typename T>
struct A {
template<typename U> void f();
};
template<typename T, typename U>
void A<T>::f() { }
现在,参数U
是怎么回事?当然编译器可能猜测它可能属于f
,但猜测并不是编译器所喜欢的:)现有规则说,根据模板的嵌套,模板子句以正确的顺序出现。一切都很清楚了。
即使有人提出如何将参数与所涉及的模板的参数相匹配(到目前为止我没有看到真正的困难),它也会不一致。因为截至目前,一个模板子句列出了相应模板接受的所有参数。很像函数参数列表。如果我们将所有内容放在一个子句中,那么明确的语义就可以被打破 - 更不用说当我们再次将定义放入类中时,模板会突然得到它自己的子句:
// provides arguments for A's parameters, then for f ones
// when it's called
A<int> a;
a.f<bool>();
当我们有单独的模板子句来捕获每个自己的参数时,这是更自然的。所以,上面错误定义的语法是
template<typename T>
template<typename U>
void A<T>::f() { }
现在,代码的读者也会立即看到这是成员模板的定义,而不是A
的(潜在意外声明但未使用的)第二个参数。