Reputation: 43
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?
Upvotes: 4
Views: 99
Reputation: 218138
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;
}
Upvotes: 3
Reputation: 29032
You can achieve your goal by making both versions templates with universal references, and only enabling the non-variadic one if the argument derives from A.
Demo : https://godbolt.org/z/jqjvcbKfP
#include <iostream>
#include <type_traits>
class A {};
class B : public A {};
template<class... Args>
void foo(Args&&... argsPack) {
std::cout << "variadic function called" << std::endl;
}
template<class T>
// Enables this version only if the condition is true
std::enable_if_t<std::is_base_of<A, std::remove_reference_t<T>>::value>
foo(T&& arg)
{
std::cout << "non-variadic function called" << std::endl;
}
int main() {
foo("Hello");
B b;
foo(b);
}
Upvotes: 3