C + + 11 emplace_back on Vector < struct > ?

考虑以下方案:

#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");
}

它不起作用:

$ 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

正确的方法是什么? 为什么?

(也试过单括号和双括号)

63289 次浏览

您必须为您的类型 T定义一个构造函数,因为它包含一个 std::string,而这个构造函数并不简单。

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

或者,仅仅使用 T{...}调用邻居响应中推荐的重载 emplace_back()... 一切都取决于您的典型用例..。

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");
}

您需要为类显式定义一个 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");
}

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

这似乎涵盖了23.2.1/13。

首先,定义:

Given a container type X having an allocator_type identical to A and a Value _ type 与 T 相同,给定一个类型为 A 的 lvalue m,指针 p 类型为 T * 的表达式 v 和类型为 T 的 rv,则 定义了以下术语。

现在,是什么让它可以安置-建设:

T is EmplaceConstructible into X from args , for zero or more 参数 args 表示下面的表达式是格式良好的: 结构(m,p,args) ;

最后关于构造调用的默认实现的一个注意事项:

注意: 容器调用 allocator _ trait: : construction (m,p,args)来 使用 args 在 p 处构造一个元素 : allocator 将调用: : new ((void *) p) T (args) ,但是是专用的 分配器可以选择不同的定义。

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

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

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). 如果您将使用叮当3.1(或其他原因) ,请尝试下一个。

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

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

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

For anyone from the future, this behavior 将被改变 in 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.