A motivação em si pode ser vista no artigo .
É necessário tornar os construtores condicionalmente explícitos. Ou seja, você quer:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
O primeiro está bom, esses construtores estão implícitos. Mas o último seria ruim, esses construtores são explicit
. Com o C ++ 17 (ou C ++ 20 com conceitos), a única maneira de fazer esse trabalho é escrever dois construtores - um explicit
e um não:
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&& );
};
Estes são quase inteiramente duplicados - e as definições desses construtores seriam idênticas.
Com explicit(bool)
, você pode simplesmente escrever um único construtor - com a parte condicionalmente explícita da construção localizada apenas no explicit
especificador-:
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&& );
};
Isso combina melhor com a intenção, é muito menos código para escrever e é menos trabalhoso para o compilador durante a resolução de sobrecarga (já que há menos construtores para escolher).
tuple
com esse recurso.