模板显式实例化如何工作以及何时工作?
How template explicit instantiation works and when?
这是 C++ primer 第 5 版的练习:
“练习 16.26:假设 NoDefault
是一个没有默认构造函数的 class,我们可以显式实例化 vector<NoDefault>
吗?如果不能,为什么不呢?”
这是我的猜测:
是的,我们可以实例化它:
template <typename T>
class Foo
{
public:
void func(){cout << x_.value_ << endl;}
private:
T x_;
};
class Bar
{
public:
Bar(int x) : value_(x){}
void print(){}
private:
int value_{};
template <class T>
friend class Foo;
};
extern template class Foo<Bar>; // instantiation declaration
template class Foo<Bar>; // instantiation definition
int main()
{
// Foo<Bar> f;
}
代码工作正常,但如果我取消注释 main 中的行,我会收到预期的错误,因为 Bar
不是默认构造的。
如果我使用相同的 class Bar
作为 std::vector
的元素类型,它不起作用:
extern template class vector<Bar>; // declaration ok
template class vector<Bar>; // instantiation: doesn't work?!
那么为什么我的 Foo<Bar>
实例化有效但 vector<Bar>
无效?
在我看来,vector<Bar>
不工作是合乎逻辑的,因为显式实例化定义实例化了 [=48= 的所有成员(甚至是未使用的成员) ] 模板;在此示例中,Foo<Bar>
的默认构造函数暗示其元素类型 Bar
的默认 ctor
;后者不提供;毕竟 Foo<Bar>()
通常被声明为已删除的成员函数,因为 x_
没有默认构造函数。所以我不知道为什么 Foo<Bar>
定义有效?!谢谢。
在标准中,[temp.explicit] 部分解释了显式实例化中发生的情况。特别是,p12 规定:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
现在,std::vector<T>
有一个构造函数,它接受一个整数 n
并用 n
value-initialized T
初始化向量。可以假定此构造函数的定义位于 <vector>
header 中的某处(请参阅 Why can templates only be implemented in the header file?)。因此 std::vector<Bar>
的显式实例化定义将使用 T
= Bar
.
实例化该构造函数
因为这是一个 显式 实例化,所以实例化的不仅是构造函数的签名,还有整个 body。这必须在某处包含对 Bar
的默认构造函数的调用(可能作为它调用的另一个函数的一部分,此时也将被实例化),因此编译错误作为显式实例化的一部分发生std::vector<Bar>
的定义。请注意,如果您 隐式 实例化 std::vector<Bar>
,它只会实例化(粗略地说)成员函数的签名。这就是为什么实际定义和使用 std::vector<Bar>
objects 是合法的,只要您不调用任何需要 Bar
默认构造函数存在的函数。
Foo<Bar>
显式实例化定义成功的原因是,当Foo<Bar>
被实例化时,编译器将其默认构造函数标记为已删除(只要有 non-default-constructible non-static 成员,这种情况就会发生)。因此,它在任何时候都不会尝试编译任何需要 Bar
默认构造函数的代码,并且不会发生错误。
这是 C++ primer 第 5 版的练习:
“练习 16.26:假设 NoDefault
是一个没有默认构造函数的 class,我们可以显式实例化 vector<NoDefault>
吗?如果不能,为什么不呢?”
这是我的猜测:
是的,我们可以实例化它:
template <typename T>
class Foo
{
public:
void func(){cout << x_.value_ << endl;}
private:
T x_;
};
class Bar
{
public:
Bar(int x) : value_(x){}
void print(){}
private:
int value_{};
template <class T>
friend class Foo;
};
extern template class Foo<Bar>; // instantiation declaration
template class Foo<Bar>; // instantiation definition
int main()
{
// Foo<Bar> f;
}
代码工作正常,但如果我取消注释 main 中的行,我会收到预期的错误,因为 Bar
不是默认构造的。
如果我使用相同的 class Bar
作为 std::vector
的元素类型,它不起作用:
extern template class vector<Bar>; // declaration ok
template class vector<Bar>; // instantiation: doesn't work?!
那么为什么我的
Foo<Bar>
实例化有效但vector<Bar>
无效?在我看来,
vector<Bar>
不工作是合乎逻辑的,因为显式实例化定义实例化了 [=48= 的所有成员(甚至是未使用的成员) ] 模板;在此示例中,Foo<Bar>
的默认构造函数暗示其元素类型Bar
的默认ctor
;后者不提供;毕竟Foo<Bar>()
通常被声明为已删除的成员函数,因为x_
没有默认构造函数。所以我不知道为什么Foo<Bar>
定义有效?!谢谢。
在标准中,[temp.explicit] 部分解释了显式实例化中发生的情况。特别是,p12 规定:
An explicit instantiation definition that names a class template specialization explicitly instantiates the class template specialization and is an explicit instantiation definition of only those members that have been defined at the point of instantiation.
现在,std::vector<T>
有一个构造函数,它接受一个整数 n
并用 n
value-initialized T
初始化向量。可以假定此构造函数的定义位于 <vector>
header 中的某处(请参阅 Why can templates only be implemented in the header file?)。因此 std::vector<Bar>
的显式实例化定义将使用 T
= Bar
.
因为这是一个 显式 实例化,所以实例化的不仅是构造函数的签名,还有整个 body。这必须在某处包含对 Bar
的默认构造函数的调用(可能作为它调用的另一个函数的一部分,此时也将被实例化),因此编译错误作为显式实例化的一部分发生std::vector<Bar>
的定义。请注意,如果您 隐式 实例化 std::vector<Bar>
,它只会实例化(粗略地说)成员函数的签名。这就是为什么实际定义和使用 std::vector<Bar>
objects 是合法的,只要您不调用任何需要 Bar
默认构造函数存在的函数。
Foo<Bar>
显式实例化定义成功的原因是,当Foo<Bar>
被实例化时,编译器将其默认构造函数标记为已删除(只要有 non-default-constructible non-static 成员,这种情况就会发生)。因此,它在任何时候都不会尝试编译任何需要 Bar
默认构造函数的代码,并且不会发生错误。