The 2nd edition of C++ Templates - The Complete Guide features the following code at page 435
#include <string>
#include <type_traits>
template<typename T, typename = void>
struct HasBeginT : std::false_type {};
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
: std::true_type {};
and comments that decltype(std::declval<T>().begin())
is used to test whether it is valid to call .begin()
on a T
.
This all makes sense, I think...
What staggers me is the comment in the footnote:
Except that
decltype(call-expression)
does not require a nonreference, non-void
return type to be complete, unlike call expressions in other contexts. Usingdecltype(std::declval<T>().begin(), 0)
instead does add the requirement that the return type of the call is complete, because the returned value is no longer the result of thedecltype
operand.
I don't really understand it.
In an attempt to play with it, I tried to see what it behaves with a void
member begin
, with the following code
struct A {
void begin() const;
};
struct B {
};
static_assert(HasBeginT<A>::value, "");
static_assert(!HasBeginT<B>::value, "");
but both assertions pass with or without the , 0
.