C++11 emplace_back 在 vector<struct> 上?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13812703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:44:09  来源:igfitidea点击:

C++11 emplace_back on vector<struct>?

c++c++11

提问by Andrew Tomazos

Consider the following program:

考虑以下程序:

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

It doesn't work:

它不起作用:

$ g++ -std=gnu++11 ./test.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34:0,
                 from /usr/include/c++/4.7/bits/allocator.h:48,
                 from /usr/include/c++/4.7/string:43,
                 from ./test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = T; _Args = {int, double, const char (&)[4]}; _Tp = T]':
/usr/include/c++/4.7/bits/alloc_traits.h:253:4:   required from ‘static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]'
/usr/include/c++/4.7/bits/alloc_traits.h:390:4:   required from ‘static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>]'
/usr/include/c++/4.7/bits/vector.tcc:97:6:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, double, const char (&)[4]}; _Tp = T; _Alloc = std::allocator<T>]'
./test.cpp:17:32:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: no matching function for call to ‘T::T(int, double, const char [4])'
/usr/include/c++/4.7/ext/new_allocator.h:110:4: note: candidates are:
./test.cpp:6:8: note: T::T()
./test.cpp:6:8: note:   candidate expects 0 arguments, 3 provided
./test.cpp:6:8: note: T::T(const T&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided
./test.cpp:6:8: note: T::T(T&&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided

What is the correct way to do this and why?

这样做的正确方法是什么,为什么?

(Also tried single and double braces)

(也试过单双大括号)

采纳答案by Red XIII

For anyone from the future, this behavior will be changedin C++20.

对于未来的任何人,这种行为C++20 中改变

In other words, even though implementation internally will still call T(arg0, arg1, ...)it will be considered as regular T{arg0, arg1, ...}that you would expect.

换句话说,即使内部实现仍然会调用T(arg0, arg1, ...)它,它也会被视为T{arg0, arg1, ...}您期望的常规。

回答by Jerry Coffin

You need to explicitly define a ctor for the class:

您需要为该类显式定义一个ctor:

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;

    T(int a, double b, string &&c) 
        : a(a)
        , b(b)
        , c(std::move(c)) 
    {}
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

The point of using emplace_backis to avoid creating a temporary object, which is then copied (or moved) to the destination. While it is also possible to create a temporary object, then pass that to emplace_back, it defeats (at least most of) the purpose. What you want to do is pass individual arguments, then let emplace_backinvoke the ctor with those arguments to create the object in place.

使用的目的emplace_back是避免创建临时对象,然后将其复制(或移动)到目的地。虽然也可以创建一个临时对象,然后将其传递给emplace_back,但它会破坏(至少大部分)目的。您想要做的是传递单个参数,然后emplace_back使用这些参数调用 ctor 以在适当的位置创建对象。

回答by rici

Of course, this is not an answer, but it shows an interesting feature of tuples:

当然,这不是答案,但它显示了元组的一个有趣特性:

#include <string>
#include <tuple>
#include <vector>

using namespace std;

using T = tuple <
    int,
    double,
    string
>;

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

回答by Mitsuru Kariya

If you do not want to (or cannot) add a constructor, specialize allocator for T (or create your own allocator).

如果您不想(或不能)添加构造函数,请专门为 T 分配分配器(或创建您自己的分配器)。

namespace std {
    template<>
    struct allocator<T> {
        typedef T value_type;
        value_type* allocate(size_t n) { return static_cast<value_type*>(::operator new(sizeof(value_type) * n)); }
        void deallocate(value_type* p, size_t n) { return ::operator delete(static_cast<void*>(p)); }
        template<class U, class... Args>
        void construct(U* p, Args&&... args) { ::new(static_cast<void*>(p)) U{ std::forward<Args>(args)... }; }
    };
}

Note: Member function construct shown above cannot compile with clang 3.1(Sorry, I don't know why). Try next one if you will use clang 3.1 (or other reasons).

注意:上面显示的成员函数构造不能用 clang 3.1 编译(对不起,我不知道为什么)。如果您将使用 clang 3.1(或其他原因),请尝试下一个。

void construct(T* p, int a, double b, const string& c) { ::new(static_cast<void*>(p)) T{ a, b, c }; }

回答by Mark B

This seems to be covered in 23.2.1/13.

这似乎包含在 23.2.1/13 中。

First, definitions:

一、定义:

Given a container type X having an allocator_type identical to A and a value_type identical to T and given an lvalue m of type A, a pointer p of type T*, an expression v of type T, and an rvalue rv of type T, the following terms are defined.

给定容器类型 X 具有与 A 相同的 allocator_type 和与 T 相同的 value_type,并给定类型 A 的左值 m、类型 T* 的指针 p、类型 T 的表达式 v 和类型 T 的右值 rv,定义了以下术语。

Now, what makes it emplace-constructible:

现在,是什么让它可以就地建造:

T is EmplaceConstructible into X from args , for zero or more arguments args, means that the following expression is well-formed: allocator_traits::construct(m, p, args);

T 是 EmplaceConstructible 从 args 到 X 的,对于零个或多个参数 args,意味着以下表达式是格式良好的: allocator_traits::construct(m, p, args);

And finally a note about the default implementation of the construct call:

最后是关于构造调用的默认实现的说明:

Note: A container calls allocator_traits::construct(m, p, args) to construct an element at p using args. The default construct in std::allocator will call ::new((void*)p) T(args), but specialized allocators may choose a different definition.

注意:容器调用 allocator_traits::construct(m, p, args) 使用 args 在 p 处构造一个元素。std::allocator 中的默认构造将调用 ::new((void*)p) T(args),但专用分配器可能会选择不同的定义。

This pretty much tells us that for a default (and potentially the only) allocator scheme you musthave defined a constructor with the proper number of arguments for the thing you're trying to emplace-construct into a container.

这几乎告诉我们,对于默认(并且可能是唯一的)分配器方案,您必须为您尝试将构造放入容器的事物定义一个具有适当数量参数的构造函数。

回答by AlexB

You can create the struct Tinstance and then move it to the vector:

您可以创建struct T实例,然后将其移动到向量:

V.push_back(std::move(T {42, 3.14, "foo"}));

回答by zaufi

you have to define a constructor for your type Tbecause it contains an std::stringwhich is not trivial.

你必须为你的类型定义一个构造函数,T因为它包含一个std::string不是微不足道的。

moreover, it would be better to define (possible defaulted) move ctor/assign (because you have a movable std::stringas member) -- this would help to move your Tmuch more efficient...

此外,最好定义(可能默认)移动 ctor/assign(因为您有一个可移动的std::string成员)——这将有助于提高您T的效率...

or, just use T{...}to call overloaded emplace_back()as recommended in neighboug response... everything depends on your typical use cases...

或者,只是按照 neighboug 响应中的建议T{...}调用重载emplace_back()……一切都取决于您的典型用例……

回答by perreal

You can use the {}syntax to initialize the new element:

您可以使用{}语法来初始化新元素:

V.emplace_back(T{42, 3.14, "foo"});

This may or may not be optimized, but it should be.

这可能会也可能不会被优化,但它应该是。

You have to define a constructor for this to work, note that with your code you can't even do:

您必须定义一个构造函数才能使其工作,请注意,使用您的代码,您甚至不能这样做:

T a(42, 3.14, "foo");

But this is what you need to have emplace work.

但这就是你需要的地方工作。

so just:

所以就:

struct T { 
  ...
  T(int a_, double b_, string c_) a(a_), b(b_), c(c_) {}
}

will make it work the desired way.

将使其以所需的方式工作。