简体   繁体   English

如何将 std::vector::emplace_back 用于向量<vector<int> >? </vector<int>

[英]how to use std::vector::emplace_back for vector<vector<int> >?

  vector<vector<int> > res;
  res.emplace_back({1,2}); // change to res.push_back({1,2}); would work

This gives me error这给了我错误

main.cpp:61:25: error: no matching function for call to ‘std::vector<std::vector<int> >::emplace_back(<brace-enclosed initializer list>)’
main.cpp:61:25: note: candidate is:
In file included from /usr/include/c++/4.7/vector:70:0,
                 from /usr/include/c++/4.7/bits/random.h:34,
                 from /usr/include/c++/4.7/random:50,
                 from /usr/include/c++/4.7/bits/stl_algo.h:67,
                 from /usr/include/c++/4.7/algorithm:63,
                 from miscalgoc.hpp:1,
                 from main.cpp:1:
/usr/include/c++/4.7/bits/vector.tcc:92:7: note: void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = std::vector<int>; _Alloc = std::allocator<std::vector<int> >]

How to make this work?如何使这项工作? Also, why an allocator is needed here?另外,为什么这里需要分配器?

The problem is that function template arguments doesn't deduce std::initializer_list from a braced-init-list (like { 1, 2 } ). 问题是函数模板参数不会从braced-init-list(如{ 1, 2 } )中推导出std::initializer_list

Example: 例:

#include <initializer_list>
#include <type_traits>


template<typename T>
void func(T arg) {
}


int main() {
    auto init_list = {1, 2};    // This works because of a special rule
    static_assert(std::is_same<decltype(init_list), std::initializer_list<int>>::value, "not same");

    func(std::initializer_list<int>{1, 2});     // Ok. Has explicit type.

    func({1, 2});   // This doesn't because there's no rule for function
                    //      template argument to deduce std::initializer_list
                    //      in this form.
}

Live example 实例

std::vector::emplace_back() is a function template with its arguments being deduced. std::vector::emplace_back()是一个函数模板,其参数是推导出来的。 So passing it {1, 2} will not work because it couldn't deduce it. 因此传递它{1, 2}将不起作用,因为它无法推断它。 Putting an explicit type to it 为它添加一个显式类型

res.emplace_back(std::initializer_list<int>{1,2});

would make it work. 会让它发挥作用。

Live example 实例

@Mark's answer is pretty correct. @Mark的答案非常正确。 Now let's consider a more practical case. 现在让我们考虑一个更实际的案例。 After some proper operations, you've collected some data with vector<int> , and feel like pushing it into vector<vector<int>> : 经过一些正确的操作后,你已经用vector<int>收集了一些数据,感觉就像将它推入vector<vector<int>>

std::vector<std::vector<int>> res;

for (int i = 0; i < 10000; ++i) {
    //
    // do something
    //
    std::vector<int> v(10000, 0);  // data acquired
    res.push_back(v);
}

It's not like assigning values you already know. 这不像分配你已经知道的价值。 Utilizing std::initializer_list is probably no longer a solution. 使用std::initializer_list可能不再是一个解决方案。 In such cases, you may use std::move (along with either emplace_back or push_back is acceptable) 在这种情况下,您可以使用std::move (可以使用emplace_backpush_back

for (int i = 0; i < 10000; ++i) {
    std::vector<int> v(10000, 0);  // will become empty afterward
    res.emplace_back(std::move(v));  // can be replaced by 
                                     // res.push_back(std::move(v));
}

The performance is more or less improved. 性能或多或少得到改善。 You can still be benefited from the concept of xvalue move-insertion, constructing objects by move-constructor rather than copying. 您仍然可以从xvalue move-insertion的概念中受益,通过move-constructor而不是复制来构造对象。

UPDATE UPDATE

The reason that res.push_back(move(v)) works is because they overload the method std::vector::push_back(value_type&& val) after C++11. 原因在于res.push_back(move(v))的工作原理是,因为它们过载的方法std::vector::push_back(value_type&& val) C ++ 11之后。 It is made to support rvalue reference deliberately. 它是故意支持右值参考。

vector<vector > res;矢量<矢量> 水库;

res.emplace_back({1,2}); res.emplace_back({1,2});

Default type detection and conversion happens only once.默认类型检测和转换只发生一次。 It does not work twice in it's use cases.它在它的用例中不起作用两次。 For this to work you need two deductions.为此,您需要进行两次扣除。

  1. {1,2} -> deduce that the container is an initializer_list of Integers. {1,2} -> 推断容器是整数的 initializer_list。

  2. res.emplace_back( any_initializer_list_of_ints ) -> here since the member element type is known to be vector of integers AND the initializer list of integers can be used to construct a vector of integers, so the initializer_list would need to be converted to vector. res.emplace_back( any_initializer_list_of_ints ) -> 这里因为已知成员元素类型是整数向量,并且整数初始化列表可用于构造整数向量,所以需要将 initializer_list 转换为向量。

The compilers don't deduce and convert it twice at the same spot.编译器不会在同一个地方对它进行两次推导和转换。 So this will never work.所以这永远行不通。

Take a look at the documentation for vector::emplace_back . 看一下vector::emplace_back文档 emplace_back tries to create a new element in your vector, by calling the constructor for the new element with the arguments passed in. So basially, when you call emplace_back({1,2}) , it tries to pass {1,2} in to a constructor, but since res is a vector of vectors of ints, it's looking at vector constructors, none of which can take a brace-enclosed initializer list. emplace_back尝试通过调用传入参数的新元素的构造函数在向量中创建一个新元素。所以基本上,当你调用emplace_back({1,2}) ,它会尝试传递{1,2} in到构造函数,但由于res是一个向量向量的向量,它正在查看向量构造函数,其中没有一个可以采用括号封闭的初始化列表。

Also, take a look at the documentation for vector::push_back . 另外,请查看vector::push_back文档 When push_back is called, it creates a default object (in this case, a vector of ints) and copies the values into it. 调用push_back ,它会创建一个默认对象(在本例中为int的向量)并将值复制到其中。 I would guess that the reason that push_back({1,2}) works is that the brace-enclosed initializer list creates a value-type, which push_back accepts. 我猜想push_back({1,2})工作原理是大括号括起来的初始化列表创建了一个值类型, push_back接受了这个值类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM