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
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. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With