Michael Michael - 3 months ago 16
C++ Question

C++ Variadic template method specialization

I have some variadic template method, which looks like this:

template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const;

template<typename ... Args>
void Invoke(const char* funcName, Args ... args) const
{
SPrimitive params[] = { args ... };
SomeOtherInvoke(funcName, params, sizeof ... (Args));
}


Here SPrimitive - just a simple struct with a constructor for any primitive type.

I want to make one more Invoke definition for some complex type. And here is my question:
Is it possible to make variadic template method specialization in c++ 11/14 ?
I mean something like this ( for simplicity lets my type will be int):

template<int ... Args>
void Invoke(const char* funcName, Args ... args)
{
int params[] = { args ... };
SomeComplexInvoke(funcName, params, sizeof ... (Args));
}


Here I want a specialization, which takes any parameters count of type int, so I can call it just like this:

Invoke("method", 2, 4 ,9);

Answer

As @Jarod42 mentioned, it should not be done with specialization. In your example you want something special if all argument types are int, so let's write a template which will check it:

template<typename ref, typename t, typename ...types>
struct all_same {
        static constexpr bool value = std::is_same<ref, t>::value && all_same<ref, types...>::value;
};

template<typename ref, typename t>
struct all_same<ref, t> {
        static constexpr bool value = std::is_same<ref, t>::value;
};

It checks whether first type argument is equal to all other type arguments. Then in Invoke we should select params type based on args... types:

template<typename ... Args>
void Invoke(const char* funcName, Args ... args)
{
    using params_type = typename std::conditional<all_same<int, Args...>::value, int, SPrimitive>::type;
    params_type params[] = { args ... };
    SomeOtherInvoke(funcName, params, sizeof ... (Args));
}

Now for the sake of demonstration let's define:

struct SPrimitive{
};

void SomeOtherInvoke(const char*, SPrimitive*, size_t) {
        std::cout << "Invoked for SPrimitive\n";
}

void SomeOtherInvoke(const char*, int*, size_t) {
        std::cout << "Invoked for int\n";
}

and call

Invoke("foo", SPrimitive());
Invoke("foo", SPrimitive(), SPrimitive());
Invoke("foo", 1, 2, 3, 4);

The output is:

Invoked for SPrimitive
Invoked for SPrimitive
Invoked for int

which is what you asked for.