Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

the weird result_of<F(Ts...)> in Andrei Alexandrescu's talk about exploding tuple

Tags:

c++11

tuples

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

like image 647
dengos Avatar asked Sep 13 '13 04:09

dengos


1 Answers

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. :)

like image 178
Quuxplusone Avatar answered Oct 30 '22 13:10

Quuxplusone