奇妙な言語の問題は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以降、上記は整形式です(のタイプb
はint
)。
以降std::swap
とstd::invoke
の両方のメンバ関数(移動建設/元に割り当て、コール後者におけるオペレータ/代理呼)のチェックに依存する必要があり、それらの両方は、この問題の解決に依存していました。