C ++ 20の前にstd :: swapがconstexprとマークされていないのはなぜですか?


14

C ++ 20ではstd::swapconstexpr関数になります。

標準ライブラリは、マーキングの点constexprで言語に遅れを取っていることを知っていますが、2017年まで<algorithm>に、他の多くのものと同様にconstexprになりました。まだ-ありstd::swapませんでした。そのマーキングを妨げる奇妙な言語の欠陥があったことを漠然と覚えていますが、詳細を忘れています。

誰かがこれを簡潔かつ明確に説明できますか?

動機:C ++ 11 / C ++ 14コードで-のstd::swap()ような関数をマークすることがなぜ悪い考えであるのかを理解する必要がありconstexprます。

回答:


11

奇妙な言語の問題はCWG 1581です。

条項15 [特別]は、特別なメンバー関数がodrで使用されたときにのみ暗黙的に定義されることを完全に明確にしています。これにより、未評価のコンテキストで定数式の問題が発生します。

struct duration {
  constexpr duration() {}
  constexpr operator int() const { return 0; }
};

// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});

ここでの問題はconstexpr duration::duration(duration&&)、このプログラムで暗黙的に定義することが許可されていないため、初期化リストの式が定数式ではないためです(定義されていないconstexpr関数を呼び出すため)。なので、プログラムの形式は正しくありません。

行#1のコメントを外すと、移動コンストラクターが暗黙的に定義され、プログラムは有効になります。距離を置いたこの不気味な行動は非常に残念です。この点で実装は異なります。

残りの問題の説明を読むことができます。

この問題の解決策は、2017年にアルバカーキのP0859で採用されました(C ++ 17の出荷後)。その問題は、両持ちすることができるためブロッカーたconstexpr std::swap(で解決P0879)及びconstexpr std::invoke(で解決P1065両方のC ++ 20のために、またCWG1581例を有します)。


私の意見では、ここで理解する最も簡単な例は、P1065で指摘されたLLVMバグレポートからのコードです。

template<typename T>
int f(T x)
{
    return x.get();
}

template<typename T>
constexpr int g(T x)
{
    return x.get();
}

int main() {

  // O.K. The body of `f' is not required.
  decltype(f(0)) a;

  // Seems to instantiate the body of `g'
  // and results in an error.
  decltype(g(0)) b;

  return 0;
}

CWG1581は constexprメンバー関数が定義されているときのすべてであり、解決により、使用時にのみ定義されることが保証されます。P0859以降、上記は整形式です(のタイプbint)。

以降std::swapstd::invokeの両方のメンバ関数(移動建設/元に割り当て、コール後者におけるオペレータ/代理呼)のチェックに依存する必要があり、それらの両方は、この問題の解決に依存していました。


では、なぜCWG-1581はスワップ関数をconstexprとしてマークすることを防止/望ましくないのですか?
アインポクルム

3
@einpoklum swapにはstd::is_move_constructible_v<T> && std::is_move_assignable_v<T>が必要ですtrue。特別なメンバー関数がまだ生成されていなければ、それは起こり得ません。
NathanOliver

@NathanOliver:これを私の答えに追加しました。
einpoklum

5

理由

(@NathanOliverによる)

constexprスワップ関数を許可するには、この関数のテンプレートをインスタンス化する前に、スワップされた型がmove-constructibleおよびmove-assignableであることを確認する必要があります。残念ながら、C ++ 20でのみ解決された言語の欠陥のため、コンパイラーに関する限り、関連するメンバー関数がまだ定義されていない可能性があるため、それを確認することはできません

年表

  • 2016:アントニーPolukhin submitts提案P0202、すべてのマークする<algorithm>などの機能をconstexpr
  • 標準委員会の中核ワーキンググループは、欠陥CWG-1581について議論します。この問題のためにconstexpr std::swap()、またconstexpr std::invoke()上記の説明を参照してください。
  • 2017:アントニー彼の提案を数回修正して除外std::swapし、他のいくつかの構成要素を修正し、これはC ++ 17に受け入れられます。
  • 2017:CWG-1581の問題の解決策はP0859として提出され、2017年に標準委員会によって承認されました(ただしC ++ 17が出荷された後)。
  • 2017年末:アントニーは、CWG-1581の解決後にconstexpr を作成するための補足提案P0879を提出しstd::swap()ます。
  • 2018:補完的な提案がC ++ 20に受け入れられました(?)。Barryが指摘するように、constexprのstd::invoke()修正も同様です。

あなたの特定のケース

move-constructibilityとmove-assignabilityをチェックせず、特にそれを保証するタイプの他のいくつかの機能を直接チェックconstexprする場合は、スワッピングを使用できます。たとえば、プリミティブ型のみで、クラスや構造体はありません。または、理論的には、チェックを行わずに、発生する可能性のあるコンパイルエラーや、コンパイラ間の不安定な動作の切り替えに対処することもできます。いずれにせよ、そのようなものに置き換えないでください。std::swap()

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.