Yes, this is correct. A type T
is explicitly constructible from an argument A
if
- It is constructible at all from
A
. That is, a hypothetical T x(a)
is valid.
- Implicit conversions are ill-formed. That is, the hypothetical function
T test() { return a; }
would be ill-formed.
std::is_constructible
tests #1, and std::is_convertible
tests the validity of #2. Hence, wanting #1 and not #2 would be is_explicitly_constructible
just as #1 and #2 would be is_implicitly_constructible
.
Such an is_explicitly_constructible
/is_implicitly_constructible
pair is how you would implement a constructor being conditionally explicit
. For instance, in libstdc++, these two constructors for optional
exist:
implicit:
template <typename _Up = _Tp,
enable_if_t<__and_<
__not_<is_same<optional<_Tp>, decay_t<_Up>>>,
is_constructible<_Tp, _Up&&>, // (*)
is_convertible<_Up&&, _Tp> // (*)
>::value, bool> = true>
constexpr optional(_Up&& __t)
explicit:
template <typename _Up = _Tp,
enable_if_t<__and_<
__not_<is_same<optional<_Tp>, decay_t<_Up>>>,
is_constructible<_Tp, _Up&&>, // (*)
__not_<is_convertible<_Up&&, _Tp>> // (*)
>::value, bool> = false>
explicit constexpr optional(_Up&& __t);
You can see that libstdc++ uses the same expressions that you do.