これは 回答 から へのフォローアップの質問です(external-to-Cの関数型をtypedefすることは可能ですか?テンプレート?
このコードは、基本的に同じエラーメッセージで、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 を出力し、コモーC/C++は「エラー:この宣言には外部の「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標準の第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_
のような奇妙な名前を呼び出すように作成されていることに注意してください。
名前のマングリングに依存する他の機能についても同様です。関数のオーバーロード。
テンプレート関数名は追加情報で装飾する必要があるため、extern "C"
は装飾をオフにします。 extern "C"
の目的は、Cリンケージで呼び出すことができる関数を宣言できるようにすることです。これは、テンプレート関数では明らかに機能しないものです。