向量<struct>上的C++ 11\templace_back?

And*_*zos 73 c++ c++11

考虑以下程序:

#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");
}
Run Code Online (Sandbox Code Playgroud)

它不起作用:

$ 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
Run Code Online (Sandbox Code Playgroud)

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

(还试过单支撑和双支撑)

Jer*_*fin 79

您需要为类明确定义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");
}
Run Code Online (Sandbox Code Playgroud)

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

  • 接受的答案违背了'emplace_back`的目的.这是正确的答案.这就是`emplace*'的工作原理.它们使用转发的参数就地构造元素.因此,需要构造函数来获取所述参数. (9认同)
  • 我认为更好的方法是写`T(int a,double b,string c):a(a),b(b),c(std :: move(c))` (8认同)
  • 不过,向量可以提供 emplace_aggr,对吧? (2认同)
  • 问题在于该结构不再是 POD (2认同)

Red*_*III 27

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

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

  • 我来自未来,可以确认:这在 GCC 和 MSVC 中确实有效。不过,不在 Clang 中:( https://compiler-explorer.com/z/qGbGvsqzj (5认同)

ric*_*ici 21

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

#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");
}
Run Code Online (Sandbox Code Playgroud)

  • 这肯定不是答案.它有什么好玩的?任何带有ctor的类型都可以这种方式安置.元组有一个ctor.op的结构没有._那是答案. (5认同)
  • @underscore_d:我不确定我是否记得3年半前我所想的每一个细节,但我认为我建议的是如果你只是使用`tuple`而不是定义POD结构,那么你得到一个免费的构造函数,这意味着您可以免费获得`emplace`语法(除此之外 - 您还可以获得字典顺序).您丢失了成员名称,但有时创建访问器的麻烦比您原本需要的其他所有样板更少.我同意Jerry Coffin的答案要比公认的好得多.几年前我也赞成了它. (5认同)
  • 是的,拼出它可以帮助我理解你的意思!好点子.我同意,当与STL为我们提供的其他东西进行权衡时,有时候这种概括是可以忍受的:我经常使用那种"对"......但有时候想知道我是否真的在网络上获得了很多,呵呵.但未来可能会出现`tuple`.感谢您的扩展! (3认同)

Mit*_*iya 11

如果您不想(或不能)添加构造函数,请为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)... }; }
    };
}
Run Code Online (Sandbox Code Playgroud)

注意:上面显示的成员函数构造不能用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 }; }
Run Code Online (Sandbox Code Playgroud)


Dav*_*ter 7

emplace_back 返回 c++17 及更高版本中的引用,因此您可以执行此操作...

#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" };
}
Run Code Online (Sandbox Code Playgroud)

  • Nice. But will it works as "emplace" or just a "push_back" with an rvalue? (2认同)

Mar*_*k B 5

这似乎在23.2.1 / 13中涵盖。

一,定义:

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

现在,是什么使其可在位构建的:

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

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

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

这几乎告诉我们,对于默认的(也可能是唯一的)分配器方案,您必须定义一个构造函数,该构造函数要对要放置到容器中的事物进行适当数量的参数传递。