【问题标题】:How does the compilation of templates work?模板的编译是如何工作的?
【发布时间】:2022-12-19 00:15:04
【问题描述】:

我正在阅读一本关于模板如何工作的书,但我很难理解模板的这种解释。

它说

当编译器看到模板的定义时,它不会生成代码.它仅在我们实例化模板的特定实例时生成代码。代码仅在我们使用模板时(而不是在我们定义模板时)生成这一事实会影响我们如何组织源代码以及何时检测到错误......要生成实例化,编译器需要具有定义一个模板的代码函数模板或类模板的成员函数。因此,与非模板代码不同,模板的标头通常包括定义和声明。

“生成代码”到底是什么意思?我不明白编译函数模板或类模板与常规函数或类相比有什么不同。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    编译器产生模板类实例化中给出的特定类型的代码。

    例如,如果您有一个模板类声明为

    template<typename T>
    class Foo
    {
    public:
         T& bar()
         {
             return subject; 
         }
    private:
         T subject;
    };
    

    一旦你有例如以下实例化

    Foo<int> fooInt;
    Foo<double> fooDouble;
    

    这些将有效地产生与您定义的类相同的可链接代码

    class FooInt
    {
    public:
         int& bar()
         {
             return subject; 
         }
    private:
         int subject;
    }
    

    和

    class FooDouble
    {
    public:
         double& bar()
         {
             return subject; 
         }
    private:
         double subject;
    }
    

    并实例化变量,例如

    FooInt fooInt;
    FooDouble fooDouble;
    

    关于模板definitions(无论模板如何,都不要与declarations混淆)需要与标头(包含)文件一起查看这一点,原因很清楚:
    如果没有看到 definition,编译器将无法生成此代码。它可以引用在链接阶段首先出现的匹配实例。

    非模板成员函数有什么允许它 在模板函数没有的标头之外定义 有?

    非模板类/成员/函数的声明为链接器提供了预定义的入口点。该定义可以从已编译目标文件 (== .cpp == compilation unit) 中看到的单个实现中得出。
    相反,给定相同或不同的模板参数,模板化类/成员/函数的声明可能会从任意编译单元实例化。这些模板参数的定义至少需要看一次。它可以是通用的或专门的。

    请注意,无论如何您都可以为特定类型专门化模板实现(包含在标头中或在特定的compilation unit)。 如果您要在您的compilation units 之一中为您的模板类提供专门化,并且不要将您的模板类与专门化以外的类型一起使用,那么也应该足以将它们链接在一起。

    我希望这个示例有助于阐明编译器的区别和所做的工作。

    【讨论】:

    • 啊……有点像……当使用模板时,编译器会为特定类型“编写函数”,这样我就不必自己动手了……这就是生成的意思?虽然我有点不明白为什么这意味着函数的声明应该包含在头文件中——
    • ......啊,我不知道我没有看到什么,但我还是不明白。非模板成员函数有什么允许在模板函数没有的头文件之外定义它? @g-makulik
    • 我知道已经过去几天了,但出于某种原因,我真的真的不明白你的解释。似乎需要对编译器的工作方式有一个很好的理解,或者我只是不知道为什么它没有陷入困境。不过感谢您的尝试,希望有一天所有这些都有意义 lol @g-makulik
    • 我假设编译器足够聪明,可以为每个提到的实例只生成一次代码?例如如果我在 FileA.cpp 和 FileB.cpp 中提到 Foo<int>,它只会生成一次代码,对吗?
    • @vexe 当然。您的假设是正确的。我的回答是否不清楚?
    【解决方案2】:

    模板是一个图案用于创建代码。当编译器看到模板的定义时,它会记录该模式。当它看到一个采用它从该模板中挖掘出它的注释,弄清楚如何在使用它的地方应用该模式,并根据该模式生成代码。

    【讨论】:

    • 这是最生动的解释!
    【解决方案3】:

    编译器在看到模板时应该做什么?为所有可能的数据类型生成所有机器代码——整数、双精度数、浮点数、字符串……可能会花费很多时间。或者只是有点懒惰并根据需要生成机器代码。

    我想后一种选择是更好的解决方案并且可以完成工作。

    【讨论】:

    • 我想我现在对那部分了解多了一点,但我仍然不明白为什么这意味着模板声明必须在头文件中 @Ed Heal
    • .cpp 文件需要了解它们。此外,编译器需要能够在遇到将它们用于适当类型时生成必要的机器代码。
    【解决方案4】:

    这里的要点是编译器不对待一个模板定义,直到它满足模板的某个实例。 (然后它可以继续,我猜,就像它有一个普通的类,这是模板类的特定情况,具有固定的模板参数。)

    您的问题的直接答案是:编译器从用户 c++ 代码生成机器代码,我认为这是 wat 在这里用“生成代码”一词表示。

    模板声明必须在头文件中,因为当编译器编译一些使用模板的源代码时,它只有头文件(包含在带有#include 宏的源代码中),但它需要整个模板定义。所以合乎逻辑的结论是模板定义必须在标题中。

    【讨论】:

      【解决方案5】:

      当您创建一个函数并对其进行编译时,编译器会为其生成代码。许多编译器不会为未使用的静态函数生成代码。

      如果您创建了一个模板化函数并且没有任何东西使用该模板(例如 std::sort),则不会生成该函数的代码。

      请记住,模板就像模板。模板告诉我们如何使用给定的模板参数生成类或函数。如果不使用模板,则不会生成任何内容。

      还要考虑到编译器在看到所有模板参数都已解析之前不知道如何实现或使用模板。

      【讨论】:

        【解决方案6】:

        它不会立即生成代码。仅在遇到该模板的实例化时才生成类或模板代码。也就是说,如果您实际上是在创建该模板定义的对象。

        本质上,模板允许您从类型中抽象出来。如果您需要模板类的两个实例化,例如 int 和 double,编译器将在您需要时为您创建其中两个类。这就是使模板如此强大的原因。

        【讨论】:

          【解决方案7】:

          您的 C++ 由编译器读取并转换为汇编代码,然后再转换为机器代码。

          模板旨在允许通用编程。如果您的代码根本不使用您的模板,编译器将不会生成关联的汇编代码。在程序中与模板关联的数据类型越多,它生成的汇编代码就越多。

          【讨论】:

            猜你喜欢
            • 2011-05-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-18
            • 1970-01-01
            • 1970-01-01
            • 2020-05-01
            相关资源
            最近更新 更多