考虑以下计划:
#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
这样做的正确方法是什么?为什么?
(也试过单支撑和双支撑)
答案 0 :(得分:74)
您需要为该类明确定义一个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来创建对象。
答案 1 :(得分:19)
当然,这不是一个答案,但它显示了一个有趣的元组特征:
#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");
}
答案 2 :(得分:19)
对于以后的任何人,此行为will be changed在C++20中。
换句话说,即使内部实现仍会调用T(arg0, arg1, ...)
,它也会被视为您期望的常规T{arg0, arg1, ...}
。
答案 3 :(得分:9)
如果您不想(或不能)添加构造函数,请为T指定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)... }; }
};
}
注意:上面显示的成员函数构造不能用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 }; }
答案 4 :(得分:4)
这似乎在23.2.1 / 13中有所介绍。
首先,定义:
给定一个容器类型X,其allocator_type与A和a相同 value_type与T相同,并给出类型A的左值m,指针p 类型T *,类型T的表达式v和类型T的rvalue rv, 以下术语已定义。
现在,是什么让它成为可以建构的:
T是EmplaceConstructible到args,为零或更多 arguments args,表示以下表达式格式正确: allocator_traits :: construct(m,p,args);
最后关于构造调用的默认实现的注释:
注意:容器调用allocator_traits :: construct(m,p,args) 使用args在p处构造一个元素。默认构造 std :: allocator将调用:: new((void *)p)T(args),但是是专门的 分配器可以选择不同的定义。
这几乎告诉我们,对于默认(可能是唯一的)分配器方案,必须定义一个构造函数,该构造函数具有适当数量的参数,用于您尝试构建的东西进入一个容器。
答案 5 :(得分:0)
您可以创建struct T
实例,然后将其移至向量:
V.push_back(std::move(T {42, 3.14, "foo"}));
答案 6 :(得分:-1)
您必须为类型T
定义构造函数,因为它包含std::string
,这并非易事。
此外,最好定义(可能的默认)移动ctor / assign(因为你有一个可移动的std::string
作为成员) - 这将有助于提高你的T
效率。 ..
或者,只需使用T{...}
按照neighboug响应中的建议调用重载emplace_back()
...一切都取决于您的典型用例......
答案 7 :(得分:-7)
您可以使用{}
语法初始化新元素:
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_) {}
}
将使其按预期方式工作。