2

In this answer I create a type trait:

template<typename T>
using to_string_t = decltype(to_string(declval<T>()));

This works just fine but I originally set out to use result_of and now it's irking me that I can't figure out how to do it.

I'm trying to replace the line above with something like this:

template<typename T>
using to_string_t = result_of<to_string(T)>;

But I get a compiler error along the lines of:

error C2275: 'T': illegal use of this type as an expression
note: see declaration of 'T'
error C2974: 'std::result_of': invalid template argument for '_Fty', type expected

I've tried several other inputs to result_of without success, can anyone help me understand what arguments result_of is expecting here?

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

1 Answers1

8

Let's patch it up. std::result_of expects only types, its result should be retrieve from its type inner typedef, and you need typename to access said typedef because it depends on a template parameter.

template<typename T>
using to_string_t = typename std::result_of<decltype(std::to_string)(T)>::type;
                    ^^^^^^^^                ^^^^^^^^                    ^^^^^^

Or in C++14, you can drop ::type and typename :

template<typename T>
using to_string_t = std::result_of_t<decltype(std::to_string)(T)>;
                                  ^^

Fine ?

main.cpp:5:68: error: decltype cannot resolve address of overloaded function

Right, std::to_string is overloaded, so we need to disambiguate it by casting it to one of its overloads.

template<typename T>
using to_string_t = typename std::result_of<decltype(static_cast<

Hold on. We need its return type to express the destination type of the cast. We're back to our starting point.

std::result_of can't deal with overloaded functions, because the function has no definite type until the overload is resolved. decltype is the only solution here, because it does apply overload resolution.

If you're wondering how std::result_of can be useful, given the above limitation : it's used for overloaded functors, i.e. classes that overload the () operator several times. As the type of the class is known, and does not depend on the call arguments, std::result_of works.

... But shouldn't std::to_string always return a std::string ??

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • 1
    `decltype(decltype(std::to_string(declval())(declval())` :) – bolov Jun 02 '15 at 12:21
  • @bolov Missing closing parentheses, but even then you're calling `std::string` as a function. Also, this hardly avoids `decltype`/`declval` :p – Quentin Jun 02 '15 at 12:25
  • sry: here it is: `std::result_of_t())(std::declval()))>` (now tested, sry about that). But yeah it uses decltype to get the result of `to_string` in order to create a type used in result_of to get... the result of `to_string`. But... it uses `result_of` :)) – bolov Jun 02 '15 at 12:31
  • @bolov Can't really argue with that... :p – Quentin Jun 02 '15 at 12:44
  • @bolov So effectively the only way to allow `result_of` to deal with the overload is to use `decltype` in the template parameter, meaning we should just use `decltype` anyway? – Jonathan Mee Jun 02 '15 at 13:46
  • @Quentin "Shouldn't `std::to_string` always return a `std::string`?" No. If I pass it an invalid argument it won't, say you pass a `string` to `to_string`. That makes a bit more sense in the context of the original question. – Jonathan Mee Jun 02 '15 at 13:49
  • 1
    @JonathanMee The only way to retrieve the function's type that `result_of` needs is to resolve the overload. Either we do that manually by casting it (but then we're providing the information we're searching for), or we use `decltype` to do the job inside its non-evaluated context. Which is to say, we're back to square 1. – Quentin Jun 02 '15 at 14:08