このページはC++29に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
C++29では、extern "C"(リンケージ指定)の中で、エイリアステンプレートや関数テンプレートなどのテンプレートを宣言できるようになる。
extern "C" {
template <class T>
using getter = void(*)(T*); // C++26までコンパイルエラー。C++29からOK
}
これによって、C言語リンケージの関数ポインタ型をテンプレートパラメータに依存する形で書けるようになり、「値の型ごとに取得関数が用意されたCのAPI」に対する汎用のラッパーのような、Cのコールバックを扱うジェネリックなコードが素直に書けるようになる。テンプレートの宣言に現れる関数型には、従来の規則どおり言語リンケージ(呼び出し規約の側面)が適用され、特殊化の名前は通常どおりマングリングされる。
C++26までこれができなかった背景には、「テンプレート・明示的特殊化・部分特殊化はC言語リンケージを持ってはならない」という規定があった。しかし、言語リンケージには関数型に適用される側面(呼び出し規約に対応する)と、関数・変数の名前に適用される側面(名前マングリングに対応する)の2つがあり、テンプレートの特殊化はそれぞれ異なる名前へマングリングされる必要があるため、名前のためにテンプレートへextern "C"を適用したいことはそもそもない。禁止によって上記のようなコードが書けなくなるだけで利点がなかったため、C++29でこの規定は削除された。
なお、テンプレート宣言の中身をリンケージ指定にする、逆の入れ子は不適格である。
template <class T>
extern "C" void f(T*); // コンパイルエラー!テンプレート宣言の中身をリンケージ指定にはできない
仕様
- テンプレート・明示的特殊化・部分特殊化はC言語リンケージを持ってはならない、という規定が削除される。これによって、リンケージ指定(
extern "C" { ... }など)の中にテンプレート宣言を書けるようになる - テンプレート宣言の中の宣言は、リンケージ指定であってはならない
- リンケージ指定の中で宣言されたテンプレートでも、宣言中の関数型には従来どおり言語リンケージが適用される。テンプレートの特殊化の名前のマングリングは、テンプレートとして通常どおり行われる
"C"・"C++"以外の文字列によるリンケージ指定(extern "Java"など)の意味は、従来どおり条件付きサポートであり処理系定義である
例
#include <iostream>
// C言語リンケージの関数ポインタ型を、テンプレートパラメータに依存する形で定義できる
extern "C" {
template <class T>
using getter = void(*)(T*);
}
// Cライブラリで提供される想定の、値の型ごとの取得関数
extern "C" void get_int(int* p) { *p = 42; }
extern "C" void get_float(float* p) { *p = 1.5f; }
// C関数ポインタを受け取る汎用のラッパー
template <class T>
T get(getter<T> f) {
T ret;
f(&ret);
return ret;
}
int main()
{
std::cout << get(get_int) << std::endl;
std::cout << get(get_float) << std::endl;
}
出力
42
1.5
この機能が必要になった背景・経緯
テンプレートのC言語リンケージを禁止する規定が使っていた「テンプレートの言語リンケージ」という概念は、規格中で定義も参照もされておらず、関数型の言語リンケージと名前の言語リンケージのどちらを指すのかが曖昧だった(CWG 1463)。また、この禁止のためにC言語リンケージの関数型をテンプレートパラメータに依存する形で書く方法がなく、Cのコールバックを扱う汎用コードでは、テンプレートパラメータに依存しない関数型をextern "C" typedefで用意してから宣言する多段の回避策などが必要だった。
本提案は、このどこにも定義のない概念に基づく禁止規定を削除することで問題を解決した。関数型への言語リンケージの適用は既存の規則で過不足なく定まり、テンプレートの特殊化の名前には名前の言語リンケージの規則が適用されないため、禁止を残す理由がなかった。
関連項目
参照
- P2243R0 Language linkage for templates
- CWG Issue 1463.
extern "C"alias templatesextern "C"のエイリアステンプレートを許可すべきという指摘。本提案によって解決された