2

I have difficulties to understand the following code

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
  // ...
}

int main()
{
    auto lambda = [](int i) { return long(i*10); };

    typedef function_traits<decltype(lambda)> traits;

    // ...

    return 0;
}

which occurs in the answer https://stackoverflow.com/a/7943765/7006673.

Here,

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>

seems to indicate a specialization of the template class

template <typename T>
struct function_traits

however, the template parameter list of the specialization template <typename ClassType, typename ReturnType, typename... Args> is not empty (i.e., is not equal to template <>). Can someone please help me to understand, what kind of specialization this is and how the template parameters ClassType, ReturnType, and Args are deduced?

Many thanks in advance.

Community
  • 1
  • 1
abraham_hilbert
  • 2,221
  • 1
  • 13
  • 30

2 Answers2

2

This is a partial specialization. It can't be an "anything" like something that just takes typename T, but it still has some variability in it, so it's not a full specialization.

Also, the things that you're looking to match up are the number of types in the original struct/class's template line and the <...> after the name in the specializations. It's kind of weird because it's asymmetrical.

In your partial specialization:

struct function_traits<ReturnType(ClassType::*)(Args...) const>

All three of the templated types combined still only create a single type - a pointer to member function. That is the same number of types as the "parent" templated type, even though that single type is broken down into 3 additional templated types in the specialization.

// this is what I'm calling the "master template", this isn't an official term
template<class A, class B, ......., class N>
class Foo{};

template</*...This can have as many (or 0) entries as you need for this specialization...*/>
class Foo</*...This must have the same number as master template above, but can be broken down into as many smaller pieces as you want...*/> {};
xaxxon
  • 19,189
  • 5
  • 50
  • 80
2

What kind of specialization this is?

This is a partial specialization. type_traits is explicitly instantiated with:

T = ReturnType(ClassType::*)(Args...) const

But this T is dependent on ReturnType, ClassType, and Args. That is why you don't have template <> in the declaration (that would be a full specialization), but a template declaration with new parameters describing a particular kind of T.

How the template parameters ClassType, ReturnType, and Args are deduced?

They are deduced when the template is instantiated with an expression that suits this specialization. Here, ReturnType(ClassType::*)(Args...) const can only be substituted with a pointer to a method.

This is an example of SFINAE.

rocambille
  • 15,398
  • 12
  • 50
  • 68
  • Thank you very much. One question remains: I thought type deduction can be done only for template parameters of functions and not for those of structs and classes -- am I wrong? – abraham_hilbert Oct 18 '16 at 14:47
  • Implicit instantiation can be done only for template parameters of functions: you explicitly pass arguments when calling the function, so their types can be implicitly deduced. Here `function_traits` is an explicit instantiation with `T=decltype(lambda)` – rocambille Oct 18 '16 at 14:52
  • Thank you! And the deduction of `ReturnType`, `ClassType` and `Args` based on `decltype(lambda)` can be done since `decltype(lambda)` is an template parameter -- is that right? – abraham_hilbert Oct 18 '16 at 15:30