模板显式实例化如何工作以及何时工作?

Mae*_*tro 7 c++ template-instantiation

这是 C++ 入门第 5 版的练习:

“练习 16.26:假设NoDefault是一个没有默认构造函数的类,我们可以显式实例化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;
}
Run Code Online (Sandbox Code Playgroud)

代码工作正常,但如果我取消注释 main 中的行,我会按预期出现错误,因为Bar它不是默认构造的。

如果我使用相同的类Bar作为元素类型,std::vector它不起作用:

extern template class vector<Bar>; // declaration ok
template class vector<Bar>; // instantiation: doesn't work?!
Run Code Online (Sandbox Code Playgroud)
  • 那么为什么我的Foo<Bar>实例化有效但无效vector<Bar>

  • 在我看来,vector<Bar>不工作是合乎逻辑的,因为显式实例化定义实例化了类模板的所有成员(甚至是未使用的成员);在这个例子中,其中的默认构造函数Foo<Bar>意味着ctor其元素类型的默认值Bar;后者不提供;毕竟Foo<Bar>()通常被声明为已删除的成员函数,因为x_没有默认构造函数。所以我不知道为什么Foo<Bar>定义有效?!谢谢你。

Bri*_*ian 5

在标准中,[temp.explicit] 部分解释了显式实例化中发生的情况。特别是,p12规定:

命名类模板特化的显式实例化定义显式实例化类模板特化,并且是仅在实例化点定义的那些成员的显式实例化定义。

现在,std::vector<T>有一个构造函数,它接受一个整数n并用nvalue-initializedT的值初始化向量。可以假设这个构造函数的定义在头文件中的某处<vector>(请参阅为什么模板只能在头文件中实现?)。因此, 的显式实例化定义std::vector<Bar>将使用T=实例化该构造函数Bar

因为这是一个显式实例化,所以实例化的不仅是该构造函数的签名,还有它的整个主体。这必须在某处包括对 的默认构造函数的调用Bar(可能作为它调用的另一个函数的一部分,此时也将被实例化),因此编译错误作为 的显式实例化定义的一部分发生std::vector<Bar>。请注意,如果您隐式实例化std::vector<Bar>,它只会实例化(粗略地说)成员函数的签名。这就是为什么实际定义和使用std::vector<Bar>对象是合法的,只要您不调用任何需要Bar存在默认构造函数的函数。

显式实例化定义Foo<Bar> 成功的原因是,在Foo<Bar>实例化时,编译器将其默认构造函数标记为已删除(每当存在不可默认构造的非静态成员时,总是会发生这种情况)。因此,它在任何时候都不会尝试编译任何需要 的默认构造函数的代码Bar,并且不会发生错误。