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.
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.
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);
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.