In C++ Templates, The Complete Guide (2e), the author gives the following example trait definition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
template<typename F, typename... Args,
typename = decltype(std::declval<F>()(std::declval<Args&&>()...))>
std::true_type isValidImpl(void*);
// fallback if helper SFINAE’d out:
template<typename F, typename... Args>
std::false_type isValidImpl(...);
// define a lambda that takes a lambda f and returns whether calling f with args is valid
inlineconstexprauto isValid = [](auto f) {
return [](auto&&... args) {
returndecltype(isValidImpl<decltype(f),
decltype(args)&&...
>(nullptr)){};
};
};
My confusion is about the following line in the isValidImpl instantiation:
decltype(args)&&...
What I don't understand is what the purpose of && is here. Is it a forwarding reference? Or just a plain rvalue reference? Is it using reference collapsing rules? Could someone explain why this is here?
Some authors write && when it isn't strictly necessary. decltype(args)... is already a list of reference types, so decltype(args)&&... is exactly the same list of types.
The pack expansion std::declval<Args&&>()... on line 2 exhibits the same pattern. The && does nothing, AFAICT, except perhaps improve code clarity in the author's opinion.
My question is what's the purpose of casting to void in the decltype expression? Earlier the author said it was to avoid an overloaded operator, like a comma operator. But I don't think the member access operator can be overloaded, can it? So why cast to void here.
Also, what are the precedence rules going on here? Is it the result of valueT(x) that's being cast to void, and then the first member is accessed? Or is it that the first member is accessed on valueT(x) and then the entire expression is cast to void?
Entry expression is cast to void, otherwise member access would be not valid.
Member access operators . and -> have higher precendence than C style cast
My question is what's the purpose of casting to void in the decltype expression?
This declytype expression in this SFINAE evaluation, means the lambda is added to overload resolution only if valueT(x) has member first
The expression is casted to void to ensure lambda return type is void, otherwise the return type would be type of first