Translation-unit-local entities (since C++20)
Translation-unit-local (TU-local) エンティティは、ローカルであるべき(他の翻訳単位で使用されない)エンティティが他の翻訳単位で公開・使用されるのを防ぐために導入されました。
Understanding C++ Modules: Part 2 からの例は、エクスポートの制約を設けない場合の問題点を示しています:
// TUローカル制約のないモジュールユニット export module Foo; import <iostream>; namespace { class LolWatchThis { // 内部リンケージ、エクスポート不可 static void say_hello() { std::cout << "Hello, everyone!\n"; } }; } export LolWatchThis lolwut() { // LolWatchThis が戻り値型として公開される return LolWatchThis(); }
// main.cpp import Foo; int main() { auto evil = lolwut(); // 'evil' は 'LolWatchThis' 型を持つ decltype(evil)::say_hello(); // 'LolWatchThis' の定義はもはや内部ではない }
目次 |
TUローカルエンティティ
エンティティは、 TUローカル である場合、
- 以下のいずれかに該当する型、関数、変数、またはテンプレート:
- class-specifier 、関数本体、または初期化子の外側で定義される無名の型、またはTU-localエンティティのみを宣言するために使用される定義型指定子(type-specifier、class-specifier、またはenum-specifier)によって導入される無名の型
- TU-localテンプレートの特殊化
- TU-localテンプレート引数を1つ以上含むテンプレートの特殊化
- その(インスタンス化された可能性のある)宣言がexposure(後述で定義)であるテンプレートの特殊化
// 内部リンケージを持つ翻訳単位ローカルなエンティティ namespace { // 無名名前空間で宣言されたすべての名前は内部リンケージを持つ int tul_var = 1; // 翻訳単位ローカルな変数 int tul_func() { return 1; } // 翻訳単位ローカルな関数 struct tul_type { int mem; }; // 翻訳単位ローカルな(クラス)型 } template<typename T> static int tul_func_temp() { return 1; } // 翻訳単位ローカルなテンプレート // 翻訳単位ローカルなテンプレート特殊化 template<> static int tul_func_temp<int>() { return 3; } // 翻訳単位ローカルな特殊化 // 翻訳単位ローカルなテンプレート引数を持つテンプレート特殊化 template <> struct std::hash<tul_type> { // 翻訳単位ローカルな特殊化 std::size_t operator()(const tul_type& t) const { return 4u; } };
|
このセクションは不完全です
理由: ルール #1.2、#2 および #5 の例が不足しています |
値またはオブジェクトは、以下のいずれかの場合に TU-local となります
- それは、TUローカルな関数、またはTUローカルな変数に関連付けられたオブジェクトであるか、それらへのポインタである、または
- それがクラス型または配列型のオブジェクトであり、その サブオブジェクト のいずれか、またはその非静的データメンバー(参照型)が参照するオブジェクトや関数のいずれかがTUローカルであり、かつ 定数式で使用可能 である場合。
static int tul_var = 1; // TUローカル変数 static int tul_func() { return 1; } // TUローカル関数 int* tul_var_ptr = &tul_var; // TUローカル: TUローカル変数へのポインタ int (* tul_func_ptr)() = &tul_func; // TUローカル: TUローカル関数へのポインタ constexpr static int tul_const = 1; // 定数式で使用可能なTUローカル変数 int tul_arr[] = { tul_const }; // TUローカル: constexpr TUローカルオブジェクトの配列 struct tul_class { int mem; }; tul_class tul_obj{tul_const}; // TUローカル: constexpr TUローカルオブジェクトのメンバを持つ
エクスポージャー
宣言Dは、以下の場合にエンティティEを 命名する
- D がそのクロージャ型が E であるラムダ式を含む場合、
- E が関数または関数テンプレートではなく、かつ D が E を表す id-expression、type-specifier、nested-name-specifier、template-name、または concept-name を含む場合、または
- E が関数または関数テンプレートであり、かつ D が E を名前とする式、または E を含むオーバーロードの集合を参照する id-expression を含む場合。
// ラムダの命名 auto x = [] {}; // decltype(x) を命名 // 非関数(テンプレート)の命名 int y1 = 1; // y1 を命名(id-expression) struct y2 { int mem; }; y2 y2_obj{1}; // y2 を命名(type-specifier) struct y3 { int mem_func(); }; int y3::mem_func() { return 0; } // y3 を命名(nested-name-specifier) template<typename T> int y4 = 1; int var = y4<y2>; // y4 を命名(template-name) template<typename T> concept y5 = true; template<typename T> void func(T&&) requires y5<T>; // y5 を命名(concept-name) // 関数(テンプレート)の命名 int z1(int arg) { std::cout << "no overload"; return 0; } int z2(int arg) { std::cout << "overload 1"; return 1; } int z2(double arg) { std::cout << "overload 2"; return 2; } int val1 = z1(0); // z1 を命名 int val2 = z2(0); // z2 を命名( int z2(int) )
宣言は、以下のいずれかの場合に exposure となります:TU-localエンティティに名前を付ける場合、無視して
- 非インライン関数または関数テンプレートの関数本体(ただし、宣言された戻り値の型が プレースホルダ型 を使用する関数の(インスタンス化された可能性のある)定義における推論された戻り値の型は除く)、
- 変数または変数テンプレートの初期化子(ただし変数の型は除く)、
- クラス定義内のfriend宣言、および
- 定数式で初期化され、 odr-use ではない、内部リンケージまたはリンケージなしの非volatile constオブジェクトまたは参照への任意の参照、
または、TU-local値で初期化されたconstexpr変数を定義します。
|
このセクションは不完全です
理由: exposuresの例が不足しています |
TUローカル制約
(インスタンス化されている可能性のある) declaration または deduction guide が、非TU-localエンティティに対するものであり、 module interface unit (private-module-fragmentが存在する場合はその外部) またはモジュールパーティション内でexposureである場合、プログラムはill-formedとなる。そのような宣言が他のいかなる文脈で行われた場合でも、非推奨とされる。
ある翻訳単位に現れる宣言が、ヘッダーユニットではない別の翻訳単位で宣言されたTU-localエンティティを指名する場合、そのプログラムは不適格である。テンプレート特殊化に対してインスタンス化された宣言は、その特殊化のインスタンス化点に現れる。
|
このセクションは不完全です
理由: 制約の例が不足しています |
例
翻訳単位 #1:
export module A; static void f() {} inline void it() { f(); } // エラー: fの露出となる static inline void its() { f(); } // OK template<int> void g() { its(); } // OK template void g<0>(); decltype(f) *fp; // エラー: f(その型ではない)はTUローカル auto &fr = f; // OK constexpr auto &fr2 = fr; // エラー: fの露出となる constexpr static auto fp2 = fr; // OK struct S { void (&ref)(); } s{f}; // OK: 値はTUローカル constexpr extern struct W { S &s; } wrap{s}; // OK: 値はTUローカルではない static auto x = []{ f(); }; // OK auto x2 = x; // エラー: クロージャ型はTUローカル int y = ([]{ f(); }(), 0); // エラー: クロージャ型はTUローカルではない int y2 = (x, 0); // OK namespace N { struct A {}; void adl(A); static void adl(int); } void adl(double); inline void h(auto x) { adl(x); } // OK、ただし特殊化は露出となる可能性がある
翻訳単位 #2:
module A; void other() { g<0>(); // OK: 特殊化は明示的にインスタンス化されている g<1>(); // エラー: インスタンス化は翻訳単位ローカルなitsを使用する h(N::A{}); // エラー: オーバーロードセットに翻訳単位ローカルなN::adl(int)が含まれる h(0); // OK: adl(double)を呼び出す adl(N::A{}); // OK; N::adl(int)は見つからず、N::adl(N::A)を呼び出す fr(); // OK: fを呼び出す constexpr auto ptr = fr; // エラー: frはここでは定数式で使用できない }
|
このセクションは不完全です
理由:例が複雑すぎるため、より良い配置が必要です |