Search code examples
c++c++11variadic-templates

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);

Solution

  • 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.