[解決済み] 空のペアの基底クラスを持つ目的は何ですか?
質問
libstdc++ の pair の実装には、次のような奇妙な点があります。
template<typename, typename> class __pair_base
{
template<typename T, typename U> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
__pair_base(const __pair_base&) = default;
__pair_base& operator=(const __pair_base&) = delete;
};
template<typename T, typename U>
struct pair
: private __pair_base<T, U>
{ /* never uses __pair_base */ };
__pair_base
は決して使われることはなく、また空であることを考えると、それもありえません。これは、特に
std::pair
は
必須
を条件付きで構造化する
pair<T, U>
が構造型である場合T
でありU
はどちらも構造型です。
プライベートベースを持つことで非構造型になるし。
どのように解決するのですか?
tl;dr これは、非常識なオーバーロード/明示規則を実装するための、実に長い一連のハックの結果です。
std::pair
を実装し、ABI 互換性を維持するための、本当に長い一連のハックの結果です。これは、C++20 のバグです。
免責事項
これは、言語レベルの洞察に満ちた啓示というよりも、標準ライブラリの作者と一緒に記憶をたどる楽しみのようなものです。これは、C++ がいかに非常に複雑になったかを示しています。 ペア を実装することがいかに困難な作業であるかがわかります。
一生懸命に歴史を再現してみましたが、私は作者の一人ではありません。
ペア・プライマー
std::pair
は単に
template<typename T, typename U>
struct pair
{
T first;
U second;
};
には8種類のコンストラクタがリストアップされています。 cppreference に記載されている 8 種類のコンストラクタがありますが、実装者にとってはそれ以上です。すべての条件付き明示的コンストラクタは、実際には 2 ということになります。
これらのコンストラクタのすべてがオーバーロードの解決に関与しているわけではありませんが、もしそうであれば、いたるところに曖昧さが生じるでしょう。その代わり、それぞれがいつオーバーロードを行うかを管理する多くのルールがあります。 すべての の組み合わせは、SFINAE が手動で記述して無効にする必要があります。
この結果、数年にわたり、5 つのバグ報告が コンストラクタだけで . 現在では 6 件になろうとしています;)
プロローグ
その 最初のバグ は、型が同じである場合のペアパラメータの変換可能性のチェックを短絡させることについてです。
template<typename T> struct B;
template<typename T> struct A
{
A(A&&) = default;
A(const B<T> &);
};
template<typename T> struct B
{
pair<A<T>, int> a;
B(B&&) = default;
};
どうやら、変換性を早くチェックしすぎると、循環依存性のために移動コンストラクタが削除され、どのように
B
の中ではまだ不完全なものです。
A
.
nonesuch
しかし、これは
は SFINAE のプロパティを変更しました。
の
pair
. これを受けて、別の修正が実施された。この実装では、以前は無効だった代入演算子が有効になっていたため、署名の変更により代入演算子を手動でオフにしました
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};
// ...
pair& operator=(
conditional_t<conjunction_v<is_copy_assignable<T>,
is_copy_assignable<U>>,
const pair&, const nonesuch&>::type)
ここで
nonesuch
はダミーの型であり、本質的にこのオーバーロードを呼び出せなくしています。あるいはそうなのでしょうか?
no_braces_nonesuch
残念ながら、これまで作成できなかった
nonesuch
pair<int, int> p = {}; // succeeds
p = {}; // fails
それでも
を中括弧で囲んで初期化します。
. ということは
delete
は過負荷の解決をしないので、これはハード的に失敗です。
修正方法は
no_braces_nonesuch
struct no_braces_nonesuch : nonesuch
{
explicit no_braces_nonesuch(const no_braces_nonesuch&) = delete;
};
は
explicit
はオーバーロードの解決に参加しないようにします。最後に、割り当ては呼び出し不可能です。それとも...?
__pair_base
v1
ありますね、残念ながら。 を初期化する別の方法があります。 未知の型
struct anything
{
template<typename T>
operator T() { return {}; }
};
anything a;
pair<int, int> p;
p = a;
著者らは、デフォルトで生成される特殊なメンバ関数を利用することで、これを簡単に解決できることに気づきました:割り当て不可能なベースがある場合、これらをまったく宣言しないことができます
class __pair_base
{
template<typename T, typename U> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
__pair_base(const __pair_base&) = default;
__pair_base& operator=(const __pair_base&) = delete;
};
すべてのユニットテストに合格し、物事は明るく見えています。知らぬ間に、邪悪なバグの影が地平線上に不気味に立ち込めています。
__pair_base
v2
ABIが壊れました。
そんなことがあり得るのか? 空のベースは最適化されます されるのでしょうか?まあ、違いますね。
pair<pair<int, int>, int> p;
残念ながら、空ベース最適化はベースクラスのサブオブジェクトが同じ型の他のサブオブジェクトと重複していない場合にのみ適用されます。この場合
__pair_base
は外側のペアと重なります。
修正方法は簡単です。
__pair_base
をテンプレート化し、それらが異なる型であることを保証します。
構造的な種類
C++20 が登場し、ペアが 構造型 . このため、プライベートベースが存在しないことが要求されます。
template<pair<int, int>>
struct S; // fails
というわけで、私たちの旅は終わりです。このことから、私は
Chandler Carruth の cppcon での簡単な調査です。
もし必要なら、誰が 1 年で C++ コンパイラを作ることができるでしょうか?どうやら、私は実装の仕方さえ知らないようです。
std::pair
.
関連
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】変数 '' を抽象型 '' と宣言できない。
-
[解決済み】Visual Studioのデバッガーエラー。プログラムを開始できません 指定されたファイルが見つかりません
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] ルール・オブ・スリーとは?
-
[解決済み] コピーアンドスワップ慣用句とは?
-
[解決済み] C++でextern "C "を使用した場合の効果は?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] エラーが発生する。ISO C++は型を持たない宣言を禁じています。
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み】デバッグアサーションに失敗しました。C++のベクトル添え字が範囲外
-
[解決済み】c++でstd::vectorを返すための効率的な方法
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。