I have two overloads:
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(const ContainerType& container);
how can I make it choose the second overload if called as e.g.
f(std::vector());
(or any other type that has e.g. begin()
method)
https://godbolt.org/z/osdEMe7rK
CodePudding user response:
Use a lvalue reference as follows:
#include <vector>
#include <functional>
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(const ContainerType& container)
{}
int main()
{
std::vector<int> vec;
f<decltype(vec)>(vec);
}
The void f(T&&... args);
declare args that are passed by rvalue reference.
Edit 1
If you want a RValue reference you can change void f(const ContainerType& container)
to:
template<typename... T>
void f(T&&... args);
template<typename ContainerType>
void f(ContainerType&& container)
{
std::cout << "call specialized f()";
}
int main()
{
f(std::vector<int>());
}
CodePudding user response:
An rvalue reference binding to an rvalue is "better" than a const lvalue reference binding to an lvalue. So you need to make both bindings equally good by changing the second overload to also take a forwarding reference:
template <typename ContainerType>
void f(ContainerType&& container);
Now, if both overloads are viable, the non-variadic one will be selected because it is "more specialized". (The "more specialized" tiebreaker is only applied if both overloads are equally good for every argument in the call.)
If you also want to constrain it so that container.begin()
must be well-formed, you can do so in C 17 as follows:
template <typename ContainerType>
auto f(ContainerType&& container) -> std::void_t<decltype(container.begin())>;
Now, this overload will be selected if there is a single argument and the constraint is satisfied. If either condition doesn't hold, the variadic overload will be selected.
(To be really pedantic, you might want to use static_cast<ContainerType&&>(container).begin()
to do perfect forwarding inside the decltype
specifier.)