关于模板的'实例化'的含义

时间:2011-09-05 06:54:06

标签: c++ templates

  

请注意,代码仅针对被调用的成员函数进行实例化。对于类模板,只有在使用成员函数时才会对其进行实例化。

以上引用来自书:Addison Wesley的 C ++模板

我想理解术语“代码实例化”的含义。这是否意味着只保留特定的内存,或只编译该代码或其他内容?

5 个答案:

答案 0 :(得分:7)

这是一个非常有趣的问题,应该在编译器处理模板的更广泛的背景下完成。基本上模板是编译器生成函数类的代码模式。模板可用于生成任何代码(从未使用过)到无限数量的实例化。

模板不能直接编译到任何后来与不同参数一起使用的实体中(在C#泛型中都是如此,但不是C ++),而是由编译器解析代码如果稍后在处理当前翻译单元时使用,则保留在存储器中。也就是说,编译器处理模板(斜体中的模板用于创建事物的模式的英语形式,而不是精确的C ++含义)将在需要时创建代码(类或函数)。 实例化是编译器确定特定模板使用的过程与特定参数集并在参数替换的过程> template 生成要编译的类或函数,最后编译成模板的二进制代码。

有两种类型的模板实例化,隐式显式,您引用的引用是关于隐式模板实例化。我将从显式模板实例化开始,因为它更简单。当您显式实例化模板(google的语法)时,您告诉编译器您希望将模板生成的代码应用于您提供的特定参数。对于 class-template ,它强制实例化所有成员函数,这基本上意味着编译器将替换类型并将其结果编译为二进制对象。

另一方面,

隐式实例化是按需执行的。当您使用类模板时,实际使用的位和部分将从模板生成并编译到翻译单元中。如果您创建变量定义std::vector<int> v;,编译器会将类型int(以及默认类型std::allocator<int>)应用于template std::vector并创建类型std::vector<int> ,但在这样做时,它不会编译所有成员函数,而只编译那些需要的函数,在这种情况下它将是默认构造函数 std::vector<int>::vector(),以及析构函数{{1 }}。其余的方法都没有编译,二进制文件中没有代码。

没有实例化所有成员函数的原因有几个,原因的复杂性从简单到深入的语言细节。对于一些较简单的,你可以考虑编译的性能(不必生成/编译所有成员函数只是因为使用其中一个将减少编译时间相当多)。稍微复杂的是,模板中的不同成员函数可能对实例化类型施加不同的要求。例如,std::vector<int>::~vector()上的operator[]要求值的类型为 default-constructible ,因为如果地图中不存在,则运算符将创建新元素另一方面,如果您始终使用mapfind,则可以将insert用于非默认可构造的类型。通过不强制编译所有成员函数,该语言允许您使用不满足所有方法的所有要求的参数的模板,只要它确实满足实际使用

我在上面的描述中使用了 used 一词,但我还没有定义它。要获得精确的定义,您必须遵循标准,但如果直接或间接地 ,您可以将其视为使用的良好近似值代码(即你调用它,或者你实例化的其他一些成员函数调用它)或者你获取成员函数的地址。

答案 1 :(得分:1)

重要的一点是,只有在某些代码调用它时,才会编译类中的模板方法。这意味着如果没有人引用它,那么无法编译的模板方法就不是问题。

就像生成运行时错误的函数不是问题,除非它被调用,在实例化时生成编译器错误的模板函数不是问题,除非有人实例化它。例如:

#include <stdio.h>
#include <string>

template<typename T>
struct Foo
{
    const T& x;

    Foo(const T& x) : x(x) {}

    template<typename S>
    operator S () const
    {
        S s;
        s = x + 1;
        return s;
    }
};

int main()
{
    std::string s = "bar";
    int i = 42;
    Foo<std::string> fs(s);
    Foo<int> fi(i);

    printf("Here we go... -> %f\n", double(fi));
    // printf("This won't compile -> %f\n", double(fs));
    return 0;
}

此代码编译,即使在实例化Foo<std::string>时,将隐式转换实例化为double也是非法的(因为x+1对于std::string是非法的。)

如果你删除了注释,那么这是必需的,程序将无法编译。

答案 2 :(得分:0)

我认为它指的是编译器甚至从未查看过未使用的模板代码。这样做的结果是,例如,您可以在类模板中使用语法错误,如果您的代码中从未使用过类,则不会导致编译错误。

答案 3 :(得分:0)

即可。 &#34;代码被实例化&#34;表示代码被放入内存(代码段)。仅当代码中的特定方法/变量引用时才会发生这种情况。

(术语 Referred 并不意味着该函数在运行时被调用。这意味着该方法存在于代码中。所以如果方法是引用然后它将被编译并且将发出该代码。)

例如,

template<typename T>
T add(T a, T b) { return a + b; }

现在,如果您在代码中引用了double类型,

double d = add(2.3, 4.6);

然后编译器将仅为add<double>()发出代码。发布add<int>()add<A>()的代码是没有意义的。

答案 4 :(得分:0)

按具体类型替换模板参数的过程称为实例化。它导致模板的实例。即编译器使用请求的类型生成模板函数/类的代码。

只有在使用(调用)成员函数或编译器根本不生成代码时,编译器才会为模板类的成员函数生成代码。这符合C ++的基本原则,您支付使用的费用

书中的陈述解释了这一点。