Why is this variadic function ambiguous?

后端 未结 3 1571
孤街浪徒
孤街浪徒 2020-12-11 09:48

This is related to my earlier post. I\'d like to know why one attempted solution didn\'t work.

template              /* A */
size_t num_         


        
相关标签:
3条回答
  • 2020-12-11 10:18

    Apropos the usefulness/uselessness of free variadic function templates: the usual use case for these is to have a variadic function parameter list, in which case a regular overload for the empty case will do just fine:

    size_t num_args()
    {
        return 0;
    }
    
    template <typename H, typename... T> /* B */
    size_t num_args (H h, T... t)
    {
        return 1 + num_args(t...);
    }
    


    EDIT:

    As far as I can see, the following abuse of enable_if ought to work as a solution to your original question:

    #include <utility>
    
    // Only select this overload in the empty case 
    template <typename... T>
    typename std::enable_if<(sizeof...(T) == 0), size_t>::type
    num_args() 
    { 
        return 0;
    }
    
    template <typename H, typename... T>
    size_t
    num_args() 
    {
        return 1 + num_args<T...>();
    }
    

    (Edit2: Reversed the order of the overloads to make the code actually compile)

    0 讨论(0)
  • 2020-12-11 10:20

    I don't understand how this is ambiguous -- A is a declaration and B is a definition of the function declared by A. Right?

    No. A is a declaration of a function template, and B is a declaration (and definition) of another function template.

    The compiler has no way to decide between the two: they both have no arguments, and the template arguments are a match for both.

    The one in the middle is an explicit total specialization of the function template declared in A.

    If you tried to make B another specialization of A:

    template <typename H, typename... T> /* B */
    size_t num_args<H, T...>()
    {
        return 1 + num_args <T...> ();
    }
    

    ... you'd end up with a partial specialization of a function template, which is not allowed.

    You can do this with the usual trick of using a class template with partial specializations and a function template that calls into the class template:

    template <typename... T>
    class Num_Args;
    
    template <>
    struct Num_Args <>
    {
        static constexpr size_t calculate() {
            return 0;
        }
    };
    
    template <typename H, typename... T>
    struct Num_Args <H, T...>
    {
        static constexpr size_t calculate() {
            return 1 + Num_Args<T...>::calculate();
        }
    };
    
    template <typename... T> /* B */
    constexpr size_t num_args ()
    {
        return Num_Args<T...>::calculate();
    }
    
    0 讨论(0)
  • 2020-12-11 10:21

    The problem here is that also catches empty lists, so since you have an empty overload as the recursion anchor, it sees both and does not know which to chose.

    0 讨论(0)
提交回复
热议问题