【问题标题】:Explicit instantiation of function template specialization函数模板特化的显式实例化
【发布时间】:2019-04-15 16:51:55
【问题描述】:

我正在尝试创建一个专门用于某些给定类型的全局函数模板。它看起来像这样:

A.h(主模板、模板特化、外部)

template <typename T> void foo() { std::cout << "default stuff" << std::endl; }
template<> void foo<int>() { std::cout << "int stuff" << std::endl; }
extern template void foo<int>();

A.cpp(显式实例化)

template void foo<int>();

B.h

void bar();

B.cpp(包括 A.h)

 void bar() { foo<int>(); }

ma​​in.cpp

foo<int>();
bar();

编译器在我身上崩溃:“'void foo()' 的多个定义。我认为 extern 应该处理这个问题。B 编译单元不应该实例化 foo,而是在链接处使用 A 实例化时间,不是吗?我在这里做错了什么?

请注意,如果我不专门化 foo,则代码编译得很好。函数特化和实例化之间是否存在某种冲突?

【问题讨论】:

    标签: c++ templates instantiation specialization explicit


    【解决方案1】:

    这里不需要extern 来禁止实例化。通过声明显式特化,您已经告诉任何调用foo&lt;int&gt; 的代码使用显式特化而不是主模板。相反,您只需在 A.h 中声明特化,然后在 A.cpp 中定义它:

    // A.h
    template <typename T> void foo() { std::cout << "default stuff" << std::endl; }
    template <> void foo<int>();
    
    // A.cpp
    template <> void foo<int>() { std::cout << "int stuff" << std::endl; }
    

    如果您想在某个翻译单元中提供主模板的显式实例化,并且不是显式特化,则使用extern 是合适的。

    【讨论】:

    • 感谢弗朗西斯和布赖恩。我实际上认为显式模板专业化也需要放入头文件中。事实并非如此是有道理的,它解决了我的问题......
    【解决方案2】:

    由于 Brian 的回答为您提供了一个可行的解决方案并解释了为什么您不需要 extern,所以我将详细说明您的情况。

    首先,您说您的编译器崩溃了,因此您认为这是编译器错误。这不是实际情况。使用您的代码,A.cppB.cppmain.cpp 都自行成功编译。没有编译错误。在 Visual Studio 2017 CE 中,直到我尝试构建程序,直到它崩溃。这是我的 IDE 的构建错误。

    1>------ Build started: Project: Temp, Configuration: Debug Win32 ------
    1>B.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
    1>main.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
    1>C:\Users\...\Temp.exe : fatal error LNK1169: one or more multiply defined symbols found
    1>Done building project "Temp.vcxproj" -- FAILED.
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    

    这里有一个Linking 错误。由于多个定义的符号,它无法链接。

    让我们检查一下您的代码:

    啊.h

    #pragma once
    #include <iostream>
    
    // Not just a declaration of void foo<T>() but also a definition!
    template<typename T> void foo() { std::cout << "default stuff\n"; }
    // Not just a declaration of void foo<int>() but also a specialized definition!
    template<> void foo<int>() { std::cout << "int stuff\n"; }
    // external explicit instantiation 
    extern template void foo<int>();
    

    A.cpp

    #include "A.h"
    // explicit instantiation 
    template void foo<int>();
    

    B.h

    #pragma once
    void bar();
    

    B.cpp

    #include "B.h"
    #include "A.h"
    
    void bar() {
        // instantiation to call foo
        foo<int>();
    }
    

    ma​​in.cpp

    #include "A.h"
    #include "B.h"
    int main() {
        // instantiation to call foo
        foo<int>();
        bar();
    
        return 0;
    }
    

    这里发生的是所有 3 个都在编译,但是当它转到链接器通过传递三个目标文件来构建单个可执行文件时,它失败了。编译器只检查语言语法 - 语法并将其转换为目标文件。链接器从编译器接收目标文件,并为变量、函数、类等创建所有需要的符号。

    它在 main 中查找并看到 #include "A.h"#include "B.h" 所以预编译器已经完成了文本替换并在页面顶部粘贴了 A.hB.h,所以所有的代码都在属于A.cppB.cpp 翻译单元的A.hB.h 现在也在main.cpp 翻译单元中。所以它看到了foo 模板对象,而且它碰巧看到了不止一个定义!它与您使用 extern 无关。为了证明这一点,我可以删除您的一些代码,但仍然会在由于多个定义而无法链接的情况下生成相同的构建错误。

    啊.h

    #pragma once
    #include <iostream>
    
    template<typename T> void foo() { std::cout << "default stuff\n"; }
    template<> void foo<int>() { std::cout << "int stuff\n"; }
    

    A.cpp

    #include "A.h"
    

    ma​​in.cpp

    #include "A.h"
    
    int main() {
        foo<int>();
    
        return 0
    }
    

    给出基本相同的构建链接错误:

    1>------ Build started: Project: Temp, Configuration: Debug Win32 ------
    1>main.cpp
    1>main.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
    1>C:\Users\...\Temp.exe : fatal error LNK1169: one or more multiply defined symbols found
    1>Done building project "Temp.vcxproj" -- FAILED.
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
    

    两者之间的唯一区别是在AB 中找到了多重定义,而在第二个实例中,即使不使用B,也会找到A 的多重定义。

    要解决此问题,请使用Brian's 回答!基本上,经验法则是将declarations 放在headersdefinitions 放在cpp 文件中,除非您的定义在特定的类或命名空间中,而不是在全局范围内。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 2011-06-23
      • 2014-02-26
      • 1970-01-01
      • 2011-10-07
      • 2013-11-04
      相关资源
      最近更新 更多