動機自体は論文で見ることができます。
コンストラクターを条件付きで明示的にする必要があります。つまり、以下が必要です。
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
前者は問題なく、これらのコンストラクターは暗黙的です。しかし、後者は悪いでしょう、それらのコンストラクタはそうexplicit
です。C ++ 17(または概念付きのC ++ 20)では、これを機能させる唯一の方法は、2つのコンストラクター(1つはexplicit
です。
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>
, int> = 0>
constexpr pair(U1&&, U2&& );
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2> &&
!(std::is_convertible_v<U1, T1> &&
std::is_convertible_v<U2, T2>)
, int> = 0>
explicit constexpr pair(U1&&, U2&& );
};
これらはほぼ完全に複製されており、これらのコンストラクターの定義は同じです。
を使用するexplicit(bool)
と、単一のコンストラクタを作成できます-構成の条件付きで明示的な部分をexplicit
-specifier だけにローカライズします。
template <typename T1, typename T2>
struct pair {
template <typename U1=T1, typename U2=T2,
std::enable_if_t<
std::is_constructible_v<T1, U1> &&
std::is_constructible_v<T2, U2>
, int> = 0>
explicit(!std::is_convertible_v<U1, T1> ||
!std::is_convertible_v<U2, T2>)
constexpr pair(U1&&, U2&& );
};
これは、意図とよりよく一致し、書き込むコードがはるかに少なく、オーバーロードの解決中にコンパイラーが行う作業が少なくなります(選択する必要があるコンストラクターが少ないため)。
tuple
、この機能を使用する場合のように、条件付きで明示的なコンストラクターを実装する方がはるかに簡単になることです。