C ++ 11 emplace_back载体<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 

什么是正确的方法来做到这一点,为什么?

(也试过单双括号)

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

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

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

你必须定义一个构造函数才能工作,注意你的代码甚至不能执行:

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

但这是你需要安置的工作。

所以就:

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

将使其工作所需的方式。

你需要为这个类明确地定义一个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(c) {} }; vector<T> V; int main() { V.emplace_back(42, 3.14, "foo"); } 

使用emplace_back是避免创build一个临时对象,然后将其复制(或移动)到目的地。 虽然也可以创build一个临时对象,然后将其传递给emplace_back ,但它会(至less大部分)失败。 你想要做的是传递个别参数,然后让emplace_back用这些参数调用ctor来创build对象。

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

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

这似乎在23.2.1 / 13中有所涉及。

一,定义:

给定容器typesX,其具有与A相同的allocator_type和与T相同的value_type,并且给定typesA的左值m,typesT *的指针p,typesT的expression式v和typesT的右值rv,下列术语被定义。

现在,是什么让它emplace-constructible:

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

最后是一个关于构造函数调用默认实现的注释:

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

这几乎告诉我们,对于一个默认的(也可能是唯一的)分配器scheme,你必须已经定义了一个构造函数,该构造函数具有正确的数量的参数,以便构build一个容器。

如果你不想(或不能)添加一个构造函数,T的专用分配器(或创build你自己的分配器)。

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

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

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

你必须为你的typesT定义一个构造函数,因为它包含一个不重要的std::string

此外,定义(可能的默认)移动ctor / assign(因为你有一个可移动的std::string作为成员)会更好 – 这将有助于移动你的T更有效率…

或者,只需使用T{...}来调用重载的emplace_back()就像在neighboug响应中推荐的那样…一切都取决于你的典型用例…