C++11 emplace_back on vector?

前端 未结 8 521
借酒劲吻你
借酒劲吻你 2020-12-02 14:58

Consider the following program:

#include 
#include 

using namespace std;

struct T
{
    int a;
    double b;
    string c;
};

         


        
相关标签:
8条回答
  • 2020-12-02 15:31

    For anyone from the future, this behavior will be changed in C++20.

    In other words, even though implementation internally will still call T(arg0, arg1, ...) it will be considered as regular T{arg0, arg1, ...} that you would expect.

    0 讨论(0)
  • 2020-12-02 15:32

    You can use the {} syntax to initialize the new element:

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

    This may or may not be optimized, but it should be.

    You have to define a constructor for this to work, note that with your code you can't even do:

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

    But this is what you need to have emplace work.

    so just:

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

    will make it work the desired way.

    0 讨论(0)
  • 2020-12-02 15:35

    This seems to be covered in 23.2.1/13.

    First, definitions:

    Given a container type X having an allocator_type identical to A and a value_type identical to T and given an lvalue m of type A, a pointer p of type T*, an expression v of type T, and an rvalue rv of type T, the following terms are defined.

    Now, what makes it emplace-constructible:

    T is EmplaceConstructible into X from args , for zero or more arguments args, means that the following expression is well-formed: allocator_traits::construct(m, p, args);

    And finally a note about the default implementation of the construct call:

    Note: A container calls allocator_traits::construct(m, p, args) to construct an element at p using args. The default construct in std::allocator will call ::new((void*)p) T(args), but specialized allocators may choose a different definition.

    This pretty much tells us that for a default (and potentially the only) allocator scheme you must have defined a constructor with the proper number of arguments for the thing you're trying to emplace-construct into a container.

    0 讨论(0)
  • 2020-12-02 15:36

    If you do not want to (or cannot) add a constructor, specialize allocator for T (or create your own 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)... }; }
        };
    }
    

    Note: Member function construct shown above cannot compile with clang 3.1(Sorry, I don't know why). Try next one if you will use clang 3.1 (or other reasons).

    void construct(T* p, int a, double b, const string& c) { ::new(static_cast<void*>(p)) T{ a, b, c }; }
    
    0 讨论(0)
  • 2020-12-02 15:36

    You can create the struct T instance and then move it to the vector:

    V.push_back(std::move(T {42, 3.14, "foo"}));
    
    0 讨论(0)
  • 2020-12-02 15:38

    you have to define a constructor for your type T because it contains an std::string which is not trivial.

    moreover, it would be better to define (possible defaulted) move ctor/assign (because you have a movable std::string as member) -- this would help to move your T much more efficient...

    or, just use T{...} to call overloaded emplace_back() as recommended in neighboug response... everything depends on your typical use cases...

    0 讨论(0)
提交回复
热议问题