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-voidreturn 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 thedecltypeoperand.
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.
Your demo uses void begin() const; to test the following
... instead does add the requirement that the return type of the call is complete ...
But a void return type is not the same as an incomplete return type. For that you could try
struct X;
struct A {
X begin() const;
};
Here, X is indeed incomplete, and now the , 0 matters. With it, the first static_assert won't pass, but it passes without the , 0.
demo
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