向量的通用参考

时间:2018-10-01 10:11:30

标签: c++

我想对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 

2 个答案:

答案 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";