c++variadicupcasting

Variadic arguments vs. upcasting arguments


The overloaded variadic version of a function is supposed to have the lowest priority, when the compiler chooses a template specialization. However, this is not the case when a function argument has to be upcasted to a base class first before it matches a template argument. See the example below.

class A {};
class B : public A {};
template<class... Args>
void foo(Args&&... argsPack) {
    std::cout << "variadic function called" << std::endl;
}
void foo(A&) {
    std::cout << "non-variadic function called" << std::endl;
}


int main() {
    foo("Hello");
    B b;
    foo(b);
}

The output of this program results in

variadic function called
variadic function called

If I wanted the non-variadic function to be called, I would have to delete the variadic overloaded function entirely. Or alternatively I would have to write a third overloaded function that accepts instances of class B directly:

void foo(B&) {
    std::cout << "non-variadic function called" << std::endl;
}

However, I want to avoid both solutions, because they are not elegant and error prone for my project. So is there a third solution for this?


Solution

  • You might constraint your template. I add extra overload to simplify the requirement:

    void foo() // Simulate empty pack
    {
        std::cout << "variadic function called" << std::endl;
    }
    
    template <typename T, class... Args>
    requires (!std::is_base_of_v<A, std::decay_t<T>> || sizeof...(Args) != 0)
    void foo(T&& t, Args&&... argsPack) {
        std::cout << "variadic function called" << std::endl;
    }
    void foo(A&) {
        std::cout << "non-variadic function called" << std::endl;
    }
    

    Demo