假设你有简单的模板函数(为了简单起见不是类成员),在同一个.h文件中具有特定于类型的特殊化...
template <class TYPE>
void some_function(TYPE& val)
{
// some generic implementation
}
template <>
inline void some_function<int>(int& val)
{
// some int specific implementation
}
除非您明确地将编译器指向inline
特化(inline
关键字),否则如果.h文件被包含多次,则会出现链接错误(至少我在Visual C ++ 2008中)。
我们都知道inline
只是对编译器的建议,它可以忽略。在这种特殊情况下,编译器是否允许忽略此建议并让链接器失败?
答案 0 :(得分:7)
如果不使用inline
,则相同的函数会使用extern
链接编译成多个.obj
文件,这会导致链接器抛出重复的符号错误。
这与编译器实际是否内联编译函数无关,因为它可以将它视为static
函数,并使每个实现对每个编译单元都是私有的。但是,您不能将static
用于此目的,因为它对成员函数有其他意义,因此inline
是您唯一的选择。
答案 1 :(得分:5)
您误解了经常提到的“忽略内联”可能性的含义。
永远不允许编译器忽略函数声明中使用的inline
说明符以及此说明符对单一定义规则(ODR)的影响。
当有人说允许编译器“忽略内联”时,它只意味着编译器不需要实际内联调用到相关函数。 “忽略内联”意味着生成对内联函数的普通(非内联)函数调用。
在任何情况下,即使编译器决定始终生成对内联函数的普通调用(即始终“忽略内联”),仍然需要将该函数视为内联以用于ODR。编译器如何做到这一点是编译器的问题。你不应该担心它。
在原始示例中,您不应该收到任何链接器错误。
答案 2 :(得分:1)
这是由标准定义的,并且编译器在这方面完全符合它的外观。你正在追求的是联系。隐式模板实例化具有“特殊”链接,如内联函数所做的那样。还有一个static(关键字),它已被弃用,有利于匿名命名空间:
namespace {
…declarations…
}
所以是的,这个专业化(在你的例子中)具有与:
相同的链接void some_other_function(int& val) {
// some int specific implementation
}
事实上,在你的例子中,编译器可能会混淆内联专业化,并说它们不匹配。因此,最好将它们标记为内联(或其他方式)。
答案 3 :(得分:0)
我相信你可以明确地将方法声明为extern,然后将特化放入.cpp。我曾经尝试过与GCC过去类似的东西,但我不记得它是如何工作的具体细节。 MSDN Magazine has an article on this可能有所帮助。
答案 4 :(得分:0)
您实际看到的是单定义规则(ODR)具有内联函数的特殊情况,因为每个TU可能都有一个定义。如果函数(例如显式int特化)不是内联的,那么在链接时会出现多个定义错误。这种内联函数仍然具有外部链接。功能模板是模板,因此遵循不同的规则。功能模板的实例化/特化是功能。
对于任何函数来说,使用内联只是一个提示,但是如果函数很短(对于任何函数)或者你只想将它保留在头文件中,你可能想要应用它。这是一个没有内联的例子:
标题文件:
template<class TYPE>
void some_function(TYPE& val) {
// some generic implementation
}
template<>
void some_function<int>(int& val);
实施(.cpp)文件:
template<>
void some_function<int>(int& val) {
// some int specific implementation
}