将左值传递给用作临时std容器的模板参数的通用引用参数时与分配器相关的错误

问题描述

我将用例简化为一个无意义的最小示例:

#include <vector>

template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
    std::vector<Container>{};
};

int main() {
    std::vector<int> v;
    f(v); // (*) ... which is "solved" by std::move-ing v here. Why?
}

进行编译。但是,当我添加一个&来使未命名的参数成为转发引用时,出现以下错误(使用g++ -std=c++11 thatfile.cpp-std=c++17进行编译)。另一方面,std::move在呼叫站点v解决了该问题。

我想了解原因。

In file included from /usr/include/c++/10.2.0/x86_64-pc-linux-gnu/bits/c++allocator.h:33,from /usr/include/c++/10.2.0/bits/allocator.h:46,from /usr/include/c++/10.2.0/vector:64,from prova.cpp:1:
/usr/include/c++/10.2.0/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<std::vector<int>&>’:
/usr/include/c++/10.2.0/bits/allocator.h:116:11:   required from ‘class std::allocator<std::vector<int>&>’
/usr/include/c++/10.2.0/bits/stl_vector.h:87:21:   required from ‘struct std::_Vector_base<std::vector<int>&,std::allocator<std::vector<int>&> >’
/usr/include/c++/10.2.0/bits/stl_vector.h:389:11:   required from ‘class std::vector<std::vector<int>&,std::allocator<std::vector<int>&> >’
prova.cpp:5:5:   required from ‘void f(Container&&) [with Container = std::vector<int>&]’
prova.cpp:10:8:   required from here
/usr/include/c++/10.2.0/ext/new_allocator.h:62:26: error: forming pointer to reference type ‘std::vector<int>&’
   62 |       typedef _TP*       pointer;
      |                          ^~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:63:26: error: forming pointer to reference type ‘std::vector<int>&’
   63 |       typedef const _TP* const_pointer;
      |                          ^~~~~~~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:103:7: error: forming pointer to reference type ‘std::vector<int>&’
  103 |       allocate(size_type __n,const void* = static_cast<const void*>(0))
      |       ^~~~~~~~
/usr/include/c++/10.2.0/ext/new_allocator.h:120:7: error: forming pointer to reference type ‘std::vector<int>&’
  120 |       deallocate(_TP* __p,size_type __t)
      |       ^~~~~~~~~~

...

解决方法

有空的时候

template<typename Container>
void f(Container&) { // using && gives compilation error... (*)
    std::vector<Container>{};
};

Constainer被推导为std::vector,一切都很好。使用时

template<typename Container>
void f(Container&&) { // using && gives compilation error... (*)
    std::vector<Container>{};
};

并且您不使用std::move,则Container被推导为std::vector&,这是一种引用类型,并且您不能创建引用类型的向量,因此得到错误。

当您使用std::move(v)时,您将std::vector&&传递给函数,因此Container再次被推导为std::vector,并且由于不是引用类型,因此代码得以编译