Reputation: 10528
Please consider the following two overloads:
template<typename T, typename ...Args>
void f(std::vector<T>&& v, Args&& ...args)
{
std::cout << "f taking a vector + parameter pack\n";
}
template<typename ...Args>
void f(Args&& ...args)
{
std::cout << "f taking a parameter pack\n";
}
Now, for the following fragment the expected overload is chosen:
std::vector<int> v{1, 2, 3};
f(std::move(v), 3.0);
(outputs: f taking a vector + parameter pack)
For the following case, the second overload is chosen:
std::vector<int> v{1, 2, 3};
f(v, 3.0);
(outputs: f taking a parameter pack)
The universal reference vector parameter binds to an lvalue reference as well, so why is it that the overload with just the parameter pack is being favored in this case? Update: see the accepted answer, the assumption that the vector parameter is a universal/forwarding reference is wrong.
Upvotes: 2
Views: 179
Reputation: 13752
As explained by the accepted answer:
the vector is not considered as Forwarding Reference (aka Universal Reference).
(Compiler knows that it is a vector, thus no template reference deduction for this part, which means that the double ampersand -- && -- is treated as rvalue reference to a vector of unknown type T).
In case you do wish to pass a vector as a Forwarding Reference you can do something like that:
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
// [1] the special case
// the vector becomes now just V and acts as Forwarding Reference
// the enable_if ensures that we deal with V that is a vector
template<typename V, typename ...Args>
typename std::enable_if<
is_vector<std::remove_const_t<std::remove_reference_t<V>>>::value
>::type
f(V&& v, Args&& ...args) {
std::cout << "f taking a vector + parameter pack\n";
}
// [2] the generic case
template<typename ...Args>
void f(Args&& ...args) {
std::cout << "f taking a parameter pack\n";
}
int main() {
// all cases below go to the 1st overload
std::vector<int> v1{1, 2, 3};
f(v1, 3);
const std::vector<int> v2{1, 2, 3};
f(v2, 3);
f(std::vector<int>{1, 2, 3}, 3);
// this goes to the 2nd overload
std::array<int, 3> a{1, 2, 3};
f(a, 3);
}
Upvotes: 2
Reputation: 172924
The universal reference vector parameter binds to an lvalue reference as well
No, it's not a universal reference.
4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward Note: in class template deduction, template parameter of a class template is never a forwarding reference (since C++17)):
So for
template<typename T, typename ...Args>
void f(std::vector<T>&& v, Args&& ...args)
note that v
is not a rvalue reference to the template paremeter T
, but std::vector<T>
. So v
is "just" an rvalue reference and then can't be bound to an lvalue.
Upvotes: 7