繁体   English   中英

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

[英]Explicit instantiation of function template specialization

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

(主要模板,模板专业化,外部)

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>();

BH

void bar();

B.cpp (包括Ah)

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

main.cpp中

foo<int>();
bar();

编译器崩溃了:“'void foo()'的多个定义。我认为应该由extern处理。B编译单元不应实例化foo,而应在链接时使用A实例化,否则我在这里怎么了?

请注意,如果我不专门研究foo,则代码可以正常编译。 函数专门化和实例化之间是否存在某种冲突?

您无需在此处使用extern来抑制实例化。 通过声明显式专业化,您已经在告诉任何调用foo<int>代码以使用显式专业化而不是主模板。 相反,您只想在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; }

如果要在某个翻译单元中提供主模板的显式实例化,而不是显式的专业化,则使用extern会很合适。

正如Brian的回答为您提供了一个可行的解决方案,并解释了您为什么不需要外部的原因,我将详细说明您的情况。

首先,您说您的编译器崩溃了,因此您以为这是编译器错误。 这不是实际情况。 按照您的代码原样, 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错误。 由于定义了多个符号,因此无法链接。

让我们来看一下您的代码:

#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>();

BH

#pragma once
void bar();

B.cpp

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

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

main.cpp中

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

    return 0;
}

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

它在main中查找,并看到#include "Ah"#include "Bh"因此预编译器已经进行了文本替换,并将AhBh粘贴在页面顶部,因此,所有在AhBh中的代码属于A.cppB.cpp转换单位的内容现在也位于main.cpp转换单位中。 因此,它看到了foo模板对象,并且碰巧看到了多个定义! 它与您使用extern无关。 为了说明这一点,我可以删除一些代码,并且由于多个定义而导致链接失败,但仍会生成相同的构建错误。

#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"

main.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答案! 基本上,经验法则是有declarationsheadersdefinitionscpp文件,除非如果你的定义是在一个特定的类或命名空间,而不是在全球范围内。

暂无
暂无

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

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