Сама мотивация видна в статье .
Необходимо сделать конструкторы условно явными. То есть вы хотите:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
Первое хорошо, эти конструкторы неявные. Но последнее было бы плохо, эти конструкторы explicit
. В C ++ 17 (или C ++ 20 с концепциями) единственный способ выполнить эту работу - написать два конструктора - один 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
которых используется эта функция.