The motivation itself can be seen in the paper.
There is a need to make constructors conditionally explicit. That is, you want:
pair<string, string> safe() {
return {"meow", "purr"}; // ok
}
pair<vector<int>, vector<int>> unsafe() {
return {11, 22}; // error
}
The former is fine, those constructors are implicit. But the latter would be bad, those constructors are explicit
. With C++17 (or C++20 with concepts), the only way to make this work is to write two constructors - one explicit
and one not:
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&& );
};
These are almost entirely duplicated - and the definitions of these constructors would be identical.
With explicit(bool)
, you can just write a single constructor - with the conditionally explicit part of the construction localized to just the 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&& );
};
This matches intent better, is much less code to write, and is less work for the compiler to do during overload resolution (since there are fewer constructors to have to pick between).
tuple
with this feature. – Hick