简体   繁体   English

功能模板专门化的显式实例化

[英]Explicit instantiation of function template specialization

I am trying to create a global function template specialized for some given types. 我正在尝试创建专门用于某些给定类型的全局函数模板。 It looks something like that: 看起来像这样:

Ah (primary template, template specialization, extern) (主要模板,模板专业化,外部)

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 (explicit instantiation) A.cpp (显式实例化)

template void foo<int>();

Bh BH

void bar();

B.cpp (includes Ah) B.cpp (包括Ah)

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

main.cpp main.cpp中

foo<int>();
bar();

The compiler crashes on me: "multiple definitions of 'void foo()'. I thought that the extern was supposed to take care of this. The B compile unit should not instantiate foo, and instead use the A instantiation at link time, no? What am I getting wrong here? 编译器崩溃了:“'void foo()'的多个定义。我认为应该由extern处理。B编译单元不应实例化foo,而应在链接时使用A实例化,否则我在这里怎么了?

Note that if I do not specialize foo, the code compiles just fine. 请注意,如果我不专门研究foo,则代码可以正常编译。 Is there some kind of conflict between function specialization and instantiation? 函数专门化和实例化之间是否存在某种冲突?

You don't need extern here to suppress instantiation. 您无需在此处使用extern来抑制实例化。 By declaring an explicit specialization, you're already telling any code that calls foo<int> to use the explicit specialization rather than the primary template. 通过声明显式专业化,您已经在告诉任何调用foo<int>代码以使用显式专业化而不是主模板。 Instead, you simply want to declare the specialization in Ah and then define it in A.cpp: 相反,您只想在Ah中声明专业化,然后在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; }

Your use of extern would be appropriate if you wanted to provide an explicit instantiation of the primary template in some translation unit, and not an explicit specialization. 如果要在某个翻译单元中提供主模板的显式实例化,而不是显式的专业化,则使用extern会很合适。

As Brian's answer gives you a working solution and explains why you don't need extern, I will elaborate a little bit more on your situation. 正如Brian的回答为您提供了一个可行的解决方案,并解释了您为什么不需要外部的原因,我将详细说明您的情况。

First, you stated that your compiler was crashing on you so you were assuming it was a compiler error. 首先,您说您的编译器崩溃了,因此您以为这是编译器错误。 This is not the actual case. 这不是实际情况。 With your code as it was, A.cpp , B.cpp & main.cpp all compiled successfully on their own. 按照您的代码原样, A.cppB.cppmain.cpp都可以自行成功编译。 There were no compilation errors. 没有编译错误。 In visual studio 2017 CE, it wasn't until I tried to build the program until it crashed. 在Visual Studio 2017 CE中,直到我尝试构建程序后才崩溃。 Here is my IDE's build error. 这是我的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 ==========

What you have here is a Linking error. 您在这里遇到的是Linking错误。 It is failing to link because of multiple defined symbols. 由于定义了多个符号,因此无法链接。

Let's go over your code: 让我们来看一下您的代码:

Ah

#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 A.cpp

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

Bh BH

#pragma once
void bar();

B.cpp B.cpp

#include "B.h"
#include "A.h"

void bar() {
    // instantiation to call foo
    foo<int>();
}

main.cpp main.cpp中

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

    return 0;
}

What is happening here is all 3 are compiling, but when it goes to the linker to build a single executable by passing the three object files it fails. 这里发生的是所有3个都在编译,但是当链接器通过传递三个目标文件来构建单个可执行文件时,它将失败。 The compiler just checks for language grammar - syntax and converts it to object files. 编译器仅检查语言语法-语法并将其转换为目标文件。 The linker receives the object files from the compiler and creates all of the needed symbols for variables, functions, classes etc. 链接器从编译器接收目标文件,并为变量,函数,类等创建所有需要的符号。

It looks in main and sees #include "Ah" and #include "Bh" So the pre compiler had already did the text substitution and pasted Ah and Bh at the top of the page, so all of the code that was in Ah & Bh that belongs to A.cpp and B.cpp translations units are also now in main.cpp translation unit. 它在main中查找,并看到#include "Ah"#include "Bh"因此预编译器已经进行了文本替换,并将AhBh粘贴在页面顶部,因此,所有在AhBh中的代码属于A.cppB.cpp转换单位的内容现在也位于main.cpp转换单位中。 So it sees the foo template objects and it happens to see more than one definition! 因此,它看到了foo模板对象,并且碰巧看到了多个定义! It has nothing to do with your use of extern. 它与您使用extern无关。 To demonstrate this, I can remove some of your code and still generate the same build error where it fails to link because of multiple definitions. 为了说明这一点,我可以删除一些代码,并且由于多个定义而导致链接失败,但仍会生成相同的构建错误。

Ah

#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 A.cpp

#include "A.h"

main.cpp main.cpp中

#include "A.h"

int main() {
    foo<int>();

    return 0
}

Give basically the same build-linking error: 给出基本相同的构建链接错误:

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 ==========

The only difference between the two is that the multiple definition is found in A & B where in the 2nd instance without even using B there is a multiple definition found A . 两者之间的唯一区别是,在AB中找到了多重定义,而在第二种情况下甚至没有使用B ,都发现了A的多重定义。

To resolve this use Brian's answer! 要解决此问题,请使用Brian's答案! Basically the rule of thumb is to have declarations in headers and definitions in cpp files unless if your definitions are in a specific class or namespace and not at the global scope. 基本上,经验法则是有declarationsheadersdefinitionscpp文件,除非如果你的定义是在一个特定的类或命名空间,而不是在全球范围内。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM