I would like to use a universal reference with vector.
template<typename T>
void foo(T&& v)
{
for(typename T::iterator i = v.begin(); i != v.end(); i++)
{
std::cout << *i << std::endl;
}
}
int main()
{
std::vector v = {0,5,4,3};
foo(std::move(v));
foo(v); //compiler error
return 0;
}
but when I use as argument of foo function v(without std::move) it cause a compiler error.
I think that in both case universal reference should work.
Error:
prog.cc: In instantiation of 'void foo(T&&) [with T = std::vector<int, std::allocator<int> >&]':
prog.cc:25:10: required from here prog.cc:16:30: error: 'std::vector<int, std::allocator<int> >&' is not a class, struct, or union type 16 | for(typename T::iterator i = v.begin(); i != v.end(); i++) | ^
prog.cc:16:30: error: 'std::vector<int, std::allocator<int> >&' is not a class, struct, or union type
When you pass an lvalue to foo
, T
is deduced as an lvalue reference due to special perfect forwarding rules:
From [temp.deduct.call] (12.9.2.1 par 3):
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
Therefore typename T::iterator
will attempt to access the iterator
type alias of an lvalue reference, which does not exist.
You can either change it to:
typename std::remove_reference_t<T>::iterator
Or simply:
for(auto i = v.begin(); i != v.end(); i++) { /* ... */ }
Or even better:
for(const auto& x : v) { std::cout << x << '\n'; }
The issue is the explicit loop:
for(typename T::iterator i = v.begin(); i != v.end(); i++)
where T
is replaced by type the template is instantiated with. For an rvalue reference, this is T
(and the function argument is an rvalue reference), for an lvalue-reference, it is T&
(and due to reference collapsing, the function argument is an lvalue reference). The line above hence tries something like typename T&::iterator i
which does not work. One way to fix this is e.g.
for(typename std::decay_t<T>::iterator i = v.begin(); i != v.end(); i++)
But the function template is also a nice demonstration of the declarative power of range based for loops, as this works for both cases without any manual removal of references:
for (auto& i : v)
std::cout << i << "\n";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With