繁体   English   中英

向量上的 C++11 emplace_back<struct> ?

[英]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

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

(也试过单双大括号)

您需要为该类显式定义一个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 以在适当的位置创建对象。

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

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

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

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

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

注意:上面显示的成员函数构造不能用 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 }; }

这似乎包含在 23.2.1/13 中。

一、定义:

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

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

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

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

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

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

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

此外,最好定义(可能默认)移动构造函数/分配(因为你有一个可移动的std::string作为成员)——这将有助于更有效地移动你的T ...

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

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

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

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

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_) {}
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM