C++11在向量上重新放置< struct>?

xfb7svmp  于 2022-12-24  发布在  其他
关注(0)|答案(8)|浏览(191)

考虑以下程序:

#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

正确的方法是什么?为什么?
(Also尝试了单大括号和双大括号)

eqzww0vc

eqzww0vc1#

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

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

3mpgtkmj2#

如果你不想(或者不能)添加构造函数,可以为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)... }; }
    };
}

注:上面显示的成员函数结构不能用clang3.1编译(对不起,我不知道为什么)。如果你要用clang3.1(或其他原因),请尝试下一个。

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

myss37ts3#

23.2.1/13中似乎涵盖了这一点。
首先,定义:
给定容器类型X具有与A相同的分配器类型和与T相同的值类型,并且给定类型A的左值m、类型T* 的指针p、类型T的表达式v和类型T的右值rv,定义以下术语。
那么,是什么使它可以建造呢?
T is EmplaceConstructable into X from args,对于零个或多个参数args,意味着下面的表达式是格式良好的:分配器特征::构造(m,p,args);
最后是关于构造调用的默认实现的说明:
注意:容器调用allocator_traits::construct(m,p,args)来使用args在p构造一个元素。std::allocator中的默认构造将调用::new((void*)p)T(args),但专用分配器可能会选择不同的定义。
这很大程度上告诉我们,对于一个默认的(可能是唯一的)分配器方案,你 * 必须 * 定义一个构造函数,该构造函数具有适当数量的参数,用于你试图将构造函数放置到容器中的东西。

rsaldnfx

rsaldnfx4#

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

bfnvny8b5#

你必须为你的类型T定义一个构造函数,因为它包含了一个std::string,这不是微不足道的。
此外,最好定义(可能是默认的)move ctor/assign(因为您有一个可移动的std::string作为成员)--这将有助于更有效地移动您的T...
或者,按照neighboug response中的建议,使用T{...}调用重载的emplace_back()......一切取决于您的典型用例......

mpgws1up

mpgws1up6#

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

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

mzaanser7#

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

zbwhf8kr

zbwhf8kr8#

对于任何来自未来的人来说,这种行为will be changedC++20
换句话说,即使实现内部仍将调用T(arg0, arg1, ...),但它将被视为您所期望的常规T{arg0, arg1, ...}

相关问题