这是an answer对Is it possible to typedef a pointer-to-extern-“C”-function type within a template?的后续问题
此代码无法使用g++
,Visual C / C ++和Comeau C / C ++进行编译,并且基本上具有相同的错误消息:
#include <cstdlib>
extern "C" {
static int do_stuff(int) {
return 3;
}
template <typename return_t_, typename arg1_t_>
struct test {
static void foo(return_t_ (*)(arg1_t_)) { }
};
}
int main()
{
test<int, int>::foo(&do_stuff);
return EXIT_SUCCESS;
}
g ++说“错误:带C链接的模板”,Visual C / C ++发出编译器错误C2894,而Comeau C / C ++说“错误:此声明可能没有extern”C“链接”。
问题是,所有人都满意:
#include <cstdlib>
extern "C" {
static int do_stuff(int) {
return 3;
}
struct test {
static void foo(int (*)(int)) { }
};
}
int main()
{
test::foo(&do_stuff);
return EXIT_SUCCESS;
}
C ++标准的第7.5节“链接规范”规定:
对于类成员的名称和类成员函数的成员函数类型,将忽略C语言链接。
它甚至给出了一个例子:
extern "C" {
class X {
void mf(); // the name of the function mf and the member
// function's type have C++ language linkage
void mf2(void(*)()); // the name of the function mf2 has C++ language
// linkage; the parameter has type pointer to C function
};
}
如果在extern“C”块中允许模板,那么实例化的成员函数将具有C ++链接。
那么,为什么C ++ 98 Standard的第14章模板状态如下:
模板名称可能具有链接(3.5)。模板,模板显式特化(14.7.3)和类模板部分特化不应具有C链接。
模板“可能”具有联系意味着什么?什么是模板链接?
为什么明确禁止使用带有C链接的模板,当一个类没问题时,模板实例化的所有成员函数(默认构造函数,析构函数和赋值运算符重载)都会有C ++链接?
模板“可能”具有联系意味着什么?什么是模板链接?
所有名称都有外部链接,内部链接或没有链接(C ++03§3.5p2),但这与语言链接不同。 (令人困惑,我知道.C ++ 0x也会通过链接大大改变一切。)用作模板参数的任何东西都需要外部链接:
void f() {
struct S {};
vector<S> v; // Not allowed as S has internal linkage.
}
请注意,C ++ 98在您引用的§14p4中有“可能”,但C ++ 03删除了“可能”,因为模板不能在给它们内部链接的上下文中声明:
void f() {
// Not allowed:
template<class T>
struct S {};
}
模板不是实际代码,它们只是编译器的指南,用于在知道模板参数后如何生成代码。因此,在您尝试使用它们之前,它们实际上并不存在。您无法提供与不存在的内容的链接。
因为extern C
禁用模板使用的名称修改
要查看模板是使用名称修改,编译和反编译实现的:
#include <cassert>
template <class C>
C f(C i) { return i; }
int main() {
f<int>(1);
f<double>(1.5);
}
有:
g++ -c -g -std=c++98 main.cpp
objdump -Sr main.o
输出包含:
int main() {
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
f<int>(1);
8: bf 01 00 00 00 mov $0x1,%edi
d: e8 00 00 00 00 callq 12 <main+0x12>
e: R_X86_64_PC32 _Z1fIiET_S0_-0x4
f<double>(1.5);
12: 48 b8 00 00 00 00 00 movabs $0x3ff8000000000000,%rax
19: 00 f8 3f
1c: 48 89 45 f8 mov %rax,-0x8(%rbp)
20: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0
25: e8 00 00 00 00 callq 2a <main+0x2a>
26: R_X86_64_PC32 _Z1fIdET_S0_-0x4
}
2a: b8 00 00 00 00 mov $0x0,%eax
2f: c9 leaveq
30: c3 retq
请注意所有callq
如何被称为奇怪的名字,如_Z1fIiET_S0_
。
对于依赖于名称修改的其他特征也是如此,例如,函数重载。
我在What is the effect of extern "C" in C++?写了一个更详细的答案
因为模板函数名称需要用附加信息进行修饰,而extern "C"
会关闭装饰。 extern "C"
的目的是能够声明可以使用C链接调用的函数,这显然不会对模板函数起作用。
因为C中没有模板。