Steven
Steven

Reputation: 333

Variadic template ambiguity - empty parameter pack

How can I remove the ambiguity of this code ? (Try it here)

template <typename T>
inline void f()
{   
}

template<typename T, typename ... Args>
inline void f()     
{
    f<Args...>();
}

int main() 
{
    f<char, double, int>();     

    return 0;
}

Here is the solution i found ... but I'm not really satisfied with it. (Try it here)

template <int N>
inline void f()
{ 
}

template<int N, typename T, typename ... Args>
inline void f()     
{
    f<42,Args...>();
}

int main() 
{
    f<42,char, double, int>();     

    return 0;
}

Is it possible to give a template specialization for an empty parameter pack ?

Upvotes: 1

Views: 527

Answers (2)

HTNW
HTNW

Reputation: 29193

You haven't specialized f at all! You've overloaded it. That is, you have two templates under the name f, and it is ambiguous which one you want to use. Consider the call f<int>(). Do you mean to instantiate the first template with T = int, or the second with T = int; Args = {}? If you want to properly specialize f, I think it actually needs to be wrapped into a class (to allow partial specialization):

template<typename T, typename... Args>
struct f_wrapper
{
    static inline void f()
    {
        f_wrapper<Args...>::f();
    }
};

Then you can use this syntax to specialize it:

template<typename T>
// the brackets v v distinguish specialization from overloading
// you can't overload classes anyway, but it's important for functions
struct f_wrapper<T>
{
    static inline void f()
    {
    }
};

Now, there is one class template named f, which has two specializations. There is no ambiguity in f_wrapper<int>::f(). The one and only template is instantiated with T = int; Args = {}, and then this is used to choose the Args = {} specialization (the second definition).

You can then proceed to wrap this:

template<typename T, typename... Args>
inline void f() { f_wrapper<T, Args...>::f(); }

Upvotes: 1

NathanOliver
NathanOliver

Reputation: 181027

What you can do is add an another template parameter to the variadic overload like

template<typename T, typename U, typename ... Args>
inline void f()     
{
    f<U, Args...>();
}

This meas the function can only be called with 2 or more parameters so there is no more ambiguity between a single parameter or a single parameter and empty pack.


Depending on what you want to achieve in the function you might also be able to switch to using a fold expression if you can use C++17

Upvotes: 4

Related Questions