La motivazione stessa può essere vista nel documento .
È necessario rendere esplicitamente condizionali i costruttori. Cioè, vuoi:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
Il primo va bene, quei costruttori sono impliciti. Ma quest'ultimo sarebbe male, lo sono quei costruttori explicit
. Con C ++ 17 (o C ++ 20 con concetti), l'unico modo per farlo funzionare è scrivere due costruttori: uno explicit
e uno no:
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&& );
};
Questi sono quasi interamente duplicati e le definizioni di questi costruttori sarebbero identiche.
Con explicit(bool)
, puoi semplicemente scrivere un singolo costruttore - con la parte esplicitamente condizionata della costruzione localizzata al solo explicit
-specificatore:
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&& );
};
Questo abbina meglio l'intento, è molto meno codice da scrivere ed è meno lavoro da fare per il compilatore durante la risoluzione del sovraccarico (poiché ci sono meno costruttori tra cui scegliere).
tuple
con questa funzione.