Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 432
Tiramisu Avatar asked Sep 01 '25 03:09

Tiramisu


1 Answers

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

like image 180
Jarod42 Avatar answered Sep 02 '25 16:09

Jarod42