有时候我会尝试遵循某些规则的逻辑,有时为什么事情发生的逻辑会违反我所知道的任何法律。
通常情况下,模板被描述为仅在编译阶段生活的模板,它完全等同于为任何给定类型foo
手写一些函数T
。
那么为什么这段代码没有编译(我现在正在使用带有gcc
和clang
的C ++ 11,但我不认为它是&#39 ;在这种情况下是相关的)?
#include <iostream>
#include <cstdint>
#include <cstdlib>
extern "C" {
template <typename T>
T foo(T t)
{
return t;
}
}
int main()
{
uint32_t a = 42;
std::cout << foo(a) << '\n';
return EXIT_SUCCESS;
}
所有逻辑失败的事情是抱怨是关于链接的,而隐含的信息是这段代码没有生成函数,它会在编译之后生成其他东西,而不是适合C风格的连接。
此代码无法编译的技术原因是什么?
答案 0 :(得分:14)
让我们从一个简单的角度来看待这个问题。至少,使用extern "C"
将删除C ++名称修改。那么,我们就有了你的模板,我们将实例化它两次。
int foo(int val);
float foo(float val);
根据C的命名规则,从链接器的角度来看,这些规则必须具有相同的名称foo
。如果它们具有相同的名称,我们无法区分它们,我们将会出错。
在C ++下,名称被破坏的规则是实现定义的。因此,C ++编译器将对这两个函数应用名称修改来区分它们。也许我们会称他们为foo_int
和foo_float
。
因为C ++可以做到这一点,所以我们没有问题。但extern "C"
要求编译器应用C命名规则。
答案 1 :(得分:3)
“联动”是一个有点误导性的术语。 extern "C"
更改的主要内容是名称重整。也就是说,它在目标文件中生成与等效C代码将产生的符号种类兼容的符号名称。这样它就可以链接与C对象代码....但它与指定static
或extern
链接不同。
但是模板没有C等价物,并且名称修改用于确保给定模板化函数的不同实例化导致不同的符号名称(以便链接器知道在给定位置使用哪一个)。
所以没有办法给模板C联动;你要求编译器做两件根本不兼容的事情。
答案 2 :(得分:0)
其他答案已经解释了为什么它在C ++方面不起作用。
从C侧有工作轮,但它们不便携。
你不能使用extern "C"
关键字,创建名称错位的函数,然后在C代码链接中找到实际的错位名称。
为了使你更容易,你也可以使用GCC的abi::__cxa_demangle()
函数和一个查找表,这样你就不需要知道被破坏的函数名称是什么(只是它们的demangled签名)。
但这真的有点躲闪。
当然,如果你只从C代码调用模板函数,它们将永远不会被实例化开始。因此,您需要确保在C ++代码中调用它们以确保它们存在于目标文件中。