我想对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;
}
但是当我将其用作foo函数v(没有std :: move)的参数时,会导致编译器错误。
我认为,在两种情况下,通用引用都应该起作用。
错误:
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
答案 0 :(得分:10)
当您将左值传递给foo
时,由于特殊的完美转发规则,T
被推导为左值引用:
来自[temp.deduct.call](12.9.2.1标准3):
转发引用是对不代表类模板的模板参数的cv不合格模板参数的右值引用(在类模板参数推导([over.match.class.deduct])期间)。 如果P是转发引用,而参数是左值,则使用类型“对A的左值引用”代替A进行类型推导。
因此,typename T::iterator
将尝试访问左值引用的iterator
类型别名。
您可以将其更改为:
typename std::remove_reference_t<T>::iterator
或者简单地:
for(auto i = v.begin(); i != v.end(); i++) { /* ... */ }
甚至更好:
for(const auto& x : v) { std::cout << x << '\n'; }
答案 1 :(得分:5)
问题是显式循环:
for(typename T::iterator i = v.begin(); i != v.end(); i++)
其中T
替换为实例化模板的类型。对于右值引用,这是T
(函数参数是右值引用),对于左值引用,它是T&
(由于引用折叠,函数参数是左值引用)。因此,上面的行尝试使用typename T&::iterator i
之类的方法,但该方法无效。解决此问题的一种方法是例如
for(typename std::decay_t<T>::iterator i = v.begin(); i != v.end(); i++)
但是函数模板也很好地展示了基于范围的for循环的声明能力,因为这适用于两种情况,而无需手动删除引用:
for (auto& i : v)
std::cout << i << "\n";