8

I just read the book "Practical C++ Metaprogramming" and it has the following example that I cannot compile. Can you help sort this out for me.

template <typename F>
struct make_tuple_of_params;

template <typename Ret, typename... Args>
struct make_tuple_of_params<Ret (Args...)>
{
   using type = std::tuple<Args...>;
};

template <typename F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F callable)
{
   make_tuple_of_params_t<F> tuple;
   /*
    ... do something with arguments in tuple...
   */
}

int main()
{
   some_magic_function([] (int, double, float) {});
}

I get a compilation error saying: 'type' is not a member of any direct or indirect base class of 'make_tuple_of_params'. It seams like the SFINAE does not work as expected since the default struct is selected. How do I fix this?

3
  • A lambda type is not a function type. Commented Nov 29, 2016 at 14:25
  • 1
    Have in mind that lambdas in c++14 can have auto parameters which can obviously be perceived as templated functors. For this kind of lambdas what would you expect your tuple type look alike? Commented Nov 29, 2016 at 14:39
  • 2
    There is no SFINAE above, just specialization template pattern matching Commented Nov 29, 2016 at 14:54

2 Answers 2

6

The type of [] (int, double, float) {} is an unnamed class type local to main, called the closure type. It is most definitely not void (int, double, float); it is in fact not a function type at all. Therefore, the specialisation for function types doesn't apply, and the primary template is selected. (Note that no SFINAE is involved in your code).

As for how to fix this: I don't think there's a fully general solution. There could be a solution/workaround for a particular some_magic_function, but that would depend on what you need that function to do.

Sign up to request clarification or add additional context in comments.

2 Comments

Regarding potential fixes: Turning the lambda into a function pointer with +[] (int, double, float) {} could be an option if nothing needs to be captured. Of course, make_tuple_of_params must match function pointers and function types then.
@0xBADF00 That is covered in the second sentence of Markus's comment.
5

For lambdas if not including lambdas with auto parameters the workaround could look as follows:

#include <tuple>
#include <typeinfo>
#include <iostream>

template <class>
struct make_tuple_of_params;

template <class Res, class Type, class... Args>
struct make_tuple_of_params<Res (Type::*)(Args...) const> {
    using type = std::tuple<Args...>;
};

template <class F>
using make_tuple_of_params_t = typename make_tuple_of_params<F>::type;

template<typename F>
void some_magic_function(F callable)
{
   make_tuple_of_params_t<decltype(&F::operator())> tuple;
   std::cout << typeid(tuple).name() << std::endl;
}

int main()
{
   some_magic_function([] (int, double, float) {});
}

[live demo]

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.