This is how std::thread
constructor declared (using Visual Studio 2015):
template<class _Fn,
class... _Args,
class = typename enable_if<
!is_same<typename decay<_Fn>::type, thread>::value>::type>
explicit thread(_Fn&& _Fx, _Args&&... _Ax)
No questions regarding _Fn
and _Args
, however, that third class = ...
confuses me totally. What does it do, how does it work and what is it for?
That is an example of conditionally enabling the overload using SFINAE.
This overload shouldn't be considered for overload resolution if the first argument is of type std::thread
.
Note that the raw source of C++ standard headers is not intended to be read. It is also not intended to be mimiced. C++ compiler implementors can do many things in their std header implementations that you cannot and should not do outside of those headers. The least of which is starting a variable with an _
followed by an upper case letter (which is banned in user code).
Examine the type of the default argument if _Fn
is a std::thread
, a reference to same, or reference to a cv modified same.
typename enable_if<
!is_same<typename decay<_Fn>::type, thread>::value>::type>
decay<_Fn>::type
strips references and cv qualifications. It also converts references-to-functions to pointers-to-function and references-to-arrays to pointers-to-first-element, but that isn't important here.
Suppose _Fn
was thread&
. I will evalulate:
typename enable_if<
!is_same<typename decay<thread&>::type, thread>::value>::type>
typename enable_if<
!is_same<thread, thread>::value>::type>
typename enable_if<
!true>::type>
typename enable_if<
false>::type>
/* substitution failure occurs */>
enable_if<B>::type
only exists if B
is true
; when _Fn
is a thread, it is false, thus there is a substitution failure during overload resolution.
SFINAE means substitution failure is not an error, and instead of the compiler complaining, it simply removes this overload from consideration. And the thread(thread const&)
(which I believe is =delete
ed) constructor is found instead.
The 3rd unnamed template parameter has a default value, which is used to satisfy the following requirement of the constructor of std::thread via SFINAE, to avoid the disturbance when the move constructor is intended to be invoked.
This constructor does not participate in overload resolution if
std::decay_t<Function>
is the same type asstd::thread
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With