La motivation elle-même peut être vue dans le document .
Il est nécessaire de rendre les constructeurs explicites conditionnellement. Autrement dit, vous voulez:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
Le premier est très bien, ces constructeurs sont implicites. Mais ce dernier serait mauvais, ces constructeurs le sont explicit
. Avec C ++ 17 (ou C ++ 20 avec des concepts), la seule façon de faire ce travail est d'écrire deux constructeurs - un explicit
et pas un:
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&& );
};
Celles-ci sont presque entièrement dupliquées - et les définitions de ces constructeurs seraient identiques.
Avec explicit(bool)
, vous pouvez simplement écrire un seul constructeur - avec la partie conditionnellement explicite de la construction localisée juste au explicit
-spécifiant:
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&& );
};
Cela correspond mieux à l'intention, est beaucoup moins de code à écrire et demande moins de travail au compilateur pendant la résolution de surcharge (car il y a moins de constructeurs à choisir).
tuple
cette fonctionnalité.