C++ Templates - The Complete Guide: Wording of footnote about decltype and return type

Viewed 116

The 2nd edition of C++ Templates - The Complete Guide features the following footnote at page 436 (my bold):

Except that decltype(call-expression) does not require a nonreference, non-void return type to be complete, unlike call expressions in other contexts. Using decltype(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 the decltype operand.

The footnote refers to the fact that decltype(std::declval<T>().begin()) is used (ineffectively, based on the footnote) to test whether it is valid to call .begin() on a T. The code that uses it is the following (with some pieces of text around it for clarity:

the trick is to formulate the expression that checks whether we can call begin() inside a decltype expression for the default value of an additional function template parameter:

#include <utility>      // for declval
#include <type_traits>  // for true_type, false_type, and void_t
// primary template:
template<typename, typename = std::void_t<>>
struct HasBeginT : std::false_type {};
// partial specialization (may be SFINAE’d away):
template<typename T>
struct HasBeginT<T, std::void_t<decltype(std::declval<T>().begin())>>
  : std::true_type {
};

Here, we use decltype(std::declval<T>().begin()) to test whether, given a value/object of type T (using std::declval to avoid any constructor being required), calling a member begin() is valid.

From this previous question of mine, I've understood that since operator, can be overloaded, the role of the , 0 is to trigger the otherwise absent overload resolution, which in turn needs the type of std::declval<T>().begin() to be complete.

However, the text from the book (see the part highlighted in bold above), doesn't mention operator,, nor overload resolution. Is that just bad wording? Or maybe it's just the same matter looked from a different perspective? Or what?

1 Answers

It seems the author forgot or disregarded the possibility of , being overloaded. The whole technique is defective in this regard, not just the wording.

So if begin() is valid and returns a complete type, but , is overloaded and can't be called for some reason, you'll get a false negative.

A more robust solution would be decltype(void(std::declval<T>().begin())).

Related