3

从我在这里发布的上一个示例中,关于何时template实例化?,我得到的答案是只有在使用模板时,编译器才会实例化它。但是看看这个例子:

template <typename T>
struct Pow{
    T operator()(T const& x){ return x * x; }
};

extern template struct Pow<int>; // explicit instantiation declaration

struct Foo{
    Pow<int> pi{};
    void fn(Pow<int>);
};
void Foo::fn(Pow<int> pw){
   // std::cout << pw(7) << '\n';
}

void bar(Pow<int> pwi){
    // std::cout << pwi(10) << '\n';
}

int main(){
    Foo f;
}
  • 正如你所看到的,我已经声明了一个显式的模板实例化Pow<int>,但还没有定义它。该程序运行良好,不会抱怨缺少Pow<int>!

  • 在上一个主题中,如果我使用模板类型作为函数定义(而不是声明)的参数类型,那么模板会被实例化,但在这里你可以看到:定义了成员函数和Foo::fn(Pow<int>)普通函数函数bar(Pow<int>)但编译器不会抱怨Pow<int>?!!!

  • 如果我取消注释上述函数中的行,程序将无法编译。那么这是否意味着Pow<int>在函数定义中用作函数参数和像 in 中的成员数据时不实例化Foo::Pow<int> pi{};

  • 我觉得很混乱:

    void f(Pow<int>); // function declaration: Pow<int> is not instantiated yet.
    void f2(Pow<int>){} // function definition: Pow<int> instantiated? 
    void f3(pow<int> Pwi){ std::cout << Pwi(10);} // function definition and usage of `Pow<int>`: Pow<int> instantiated?
    
  • 主要:

    Foo f; // f has pi of type Pow<int>. so Pow<int> is instantiated? 
    
4

2 回答 2

2

该程序运行良好,不会抱怨缺少Pow<int>!

因为它没有丢失。两种形式的显式实例化(声明和定义)都会导致类模板的实例化。显式实例化定义会导致成员函数的实例化(通常仅在需要时才延迟实例化)。另一方面,显式实例化声明抑制成员函数的隐式实例化。即使那个成员函数体是可见的!

它是一种为一组受限制的类型编写模板,同时隐藏它们的实现的工具。它允许这样做:

//pow.h

template <typename T>
struct Pow{
    T operator()(T const& x); // Just a declaration 
};

extern template struct Pow<int>; // The class is instantiated, the member is 
                                 // assumed to be explicitly instantiated elsewhere

//pow.cpp
#include "pow.h"

template <typename T>
T Pow<T>::operator()(T const& x) { return x * x; }

template struct Pow<int>; // Explicit instantiation. The member function is defined
                          // in **this** translation unit.

这正是您的程序无法链接的原因。该成员operator()永远不会在您的程序中的任何地方实例化。您可以通过在某处提供显式实例化定义来修复它。例如

template <typename T>
struct Pow{
    T operator()(T const& x){ return x * x; }
};

// ...

int main() {
// ...
}

template struct Pow<int>; // Now the member function is emitted

另一个用途是潜在地提高编译时间。如果你有一个你知道经常为一组特定类型实例化的类模板,你可以帮助链接器。

// my_string.h

template<typename charT>
class my_string {
  // Everything, members and all
};

extern template class my_string<char>;
extern template class my_string<wchar_t>;

// my_string.cpp 

#include <my_string.h>

// This file can be part of a shared object that ships with your library
// The common declarations are here

template class my_string<char>;
template class my_string<wchar_t>;

现在,链接器不必对由常用的隐式实例化产生的许多重复符号进行排序my_string<char>and my_string<wchar_t>

于 2021-10-20T22:04:25.370 回答
1

Pow<int> 定义。它被定义Pow<T>T=int

于 2021-10-20T21:31:46.890 回答