template<class _Other1,
class _Other2,
class = enable_if_t<is_constructible<_Ty1, _Other1>::value
&& is_constructible<_Ty2, _Other2>::value>,
enable_if_t<is_convertible<_Other1, _Ty1>::value
&& is_convertible<_Other2, _Ty2>::value, int> = 0>
constexpr pair(pair<_Other1, _Other2>&& _Right)
_NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1>::value
&& is_nothrow_constructible<_Ty2, _Other2>::value))
: first(_STD forward<_Other1>(_Right.first)),
second(_STD forward<_Other2>(_Right.second))
{ // construct from moved compatible pair
}
template<class _Other1,
class _Other2,
class = enable_if_t<is_constructible<_Ty1, _Other1>::value
&& is_constructible<_Ty2, _Other2>::value>,
enable_if_t<!is_convertible<_Other1, _Ty1>::value
|| !is_convertible<_Other2, _Ty2>::value, int> = 0>
constexpr explicit pair(pair<_Other1, _Other2>&& _Right)
_NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1>::value
&& is_nothrow_constructible<_Ty2, _Other2>::value))
: first(_STD forward<_Other1>(_Right.first)),
second(_STD forward<_Other2>(_Right.second))
{ // construct from moved compatible pair
}
utility file for VS 2017 line 206,
_Other1 and _Other2 are parameters, this is std::pair's construction func,
and we are using Other1 and Other2 to initialize "first" and "second",
I think is_constructible is enough, why are we using is_convertible here?
and by the way, what's the difference between class = enable_if_t< ... ::value>
and enable_if_t< ... ::value,int> = 0
?
The goal here is to properly handle
explicit
construction. Consider just doing the former and trying to write a wrapper (usingREQUIRES
here to hide whatever approach to SFINAE you want):If that's all we had, then:
We definitely don't want that last one to be okay - that breaks the expectation for
Exp
!Now,
s_constructible<T, U&&>
is true if it's possible to direct-initialize aT
from aU&&
- ifT(std::declval<U&&>())
is a valid expression.is_convertible<U&&, T>
, on the other hand, checks if it is possible to copy-initialize aT
from aU&&
. That is, ifT copy() { return std::declval<U&&>(); }
is valid.The difference is that the latter does not work if the conversion is
explicit
:In order to correctly propagate explicitness, we need to use both traits together - and we can create meta-traits out of them:
These two traits are disjoint, so we can write two constructor templates that are definitely not both viable, where one constructor is explicit and the other is not:
This gives us the desired behavior:
This is what the implementation is doing here - except instead of the two meta-traits they have all of the conditions written out
explicit
ly.