6

Has anyone watched Andrei Alexandrescu's talk about exploding tuple in GoingNative2013 yet?

Here is the piece of code I don't quite follow:

template <class F, class... Ts>
auto explode(F&& f, const tuple<Ts...>& t)
    -> typename result_of<F(Ts...)>::type
{
    return Expander<sizeof...(Ts),
       typename result_of<F(Ts...)>::type,
       F,
       const tuple<Ts...>&>::expand(f, t);
}

the F(Ts...) in result_of trouble me much. I mean: doesn't F stands for a function type ? I know R(Ts...) well, but the R here is a return type, but using F in place where R should be, that's the thing driving me crazy...

Can anyone help me understand the weird F(Ts...) here ?

Here is the link forward to Andrei Alexandrescu's talk: http://channel9.msdn.com/Events/GoingNative/2013/The-Way-of-the-Exploding-Tuple

dengos
  • 103
  • 4
  • 2
    See [this question](http://stackoverflow.com/questions/15486951/why-does-stdresult-of-take-an-unrelated-function-type-as-a-type-argument) for more info about `result_of` and its admittedly funky syntax. – JohannesD Sep 13 '13 at 07:05
  • 3
    FYI: Stephan T. Lavavej explained in the last five minutes of his talk about [rand() Considered Harmful](http://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful) how `explode` is implemented correctly. – nosid Sep 13 '13 at 08:21
  • 1
    `result_of::types` means the return type of calling an object of type `F` with arguments of type `Ts`. If you understand `decltype` then it is similar to `decltype(std::declval()(std::declval()...)`. – Simple Sep 13 '13 at 09:47

1 Answers1

1

The question you want to ask is probably a duplicate of this one: Why does std::result_of take an (unrelated) function type as a type argument?

Let's dissect:

std::result_of<F(Ts...)>::type

So, somewhere in namespace std, we've got a class template result_of<>. It takes one template type parameter; i.e., it looks basically like this:

template<typename Foo>
struct result_of
{
    typedef FOOBARBAZ type;
};

Okay, so, we're instantiating this template with the parameter F(Ts...). That's unusual syntax! You presumably know that Ts is a parameter pack, and therefore the Ts... inside the parentheses will expand at compile time to a comma-separated list of types, for example int, double, bool. So we've got F(int, double, bool). Okay, that's a function type.

Just as int(char) means "function taking char and returning int", so does F(int, double, bool) mean "function taking int, double, bool and returning F".

"But wait," you say. "I thought F was already my function type!"

Yes. F is your function type. But the type expected by std::result_of is, really!, that function type wrapped up in another function type. To elaborate:

typedef int (*F)(char);
typedef F G(char);
static_assert(std::is_same< std::result_of<G>::type, int >::value);
static_assert(std::is_same< std::result_of<F(char)>::type, int >::value);
static_assert(std::is_same< std::result_of<int (*(char))(char)>::type, int >::value);

Each of the above lines is exactly equivalent: F(char) is just a much more aesthetically pleasing way of writing int (*(char))(char). Of course, you can't always get away with it, because sometimes F is a function type that can't be returned from a function:

typedef int F(char);
std::result_of<F(char)>;  // fails to compile

As @Simple wrote in the comments, std::result_of<F(Ts...)>::type can always be replaced with the less clever but also less confusing expression

decltype( std::declval<F>() ( std::declval<Ts>()... ) )

i.e., "the decltype of the result of calling a value of type F with arguments of types Ts.... Here, there are no wacky higher-level function types; everything just works the way you'd naturally expect it to. Personally, I would probably use the decltype approach in my own code, just because it's easier to understand; but I imagine that some people would prefer the std::result_of approach because it looks superficially simpler and is blessed by the Standard. To each his own. :)

Community
  • 1
  • 1
Quuxplusone
  • 23,928
  • 8
  • 94
  • 159