131

Consider the following program:

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

It doesn't work:

$ 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

What is the correct way to do this and why?

(Also tried single and double braces)

6
  • 4
    That will work if you provide an appropriate constructor.
    – chris
    Commented Dec 11, 2012 at 2:34
  • 3
    Is there a way to construct it in-place with the automatically created brace struct constructor used by T t{42,3.14, "foo"}? Commented Dec 11, 2012 at 2:38
  • 4
    I don't think that takes the form of a constructor. It's aggregate initialization.
    – chris
    Commented Dec 11, 2012 at 2:41
  • 6
    See Why doesn't emplace_back() use uniform initialization?.
    – Jesse Good
    Commented Dec 11, 2012 at 3:04
  • 6
    I am not trying to affect your opinion in any way.. But in case you did not give attention to thin question from a while.. The accepted answer, with full respect to its writer, is not an answer at all to your question and may mislead the readers. Commented Nov 8, 2016 at 18:16

8 Answers 8

134

You need to explicitly define a ctor for the class:

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

The point of using emplace_back is to avoid creating a temporary object, which is then copied (or moved) to the destination. While it is also possible to create a temporary object, then pass that to emplace_back, it defeats (at least most of) the purpose. What you want to do is pass individual arguments, then let emplace_back invoke the ctor with those arguments to create the object in place.

5
  • 17
    I think better way would be to write T(int a, double b, string c) : a(a), b(b), c(std::move(c))
    – balki
    Commented Apr 24, 2013 at 17:30
  • 9
    The accepted answer defeats the purpose of emplace_back. This is the correct answer. This is how emplace* work. They construct the element in-place using the forwarded arguments. Hence, a constructor is needed to take said arguments. Commented Jul 13, 2016 at 21:28
  • 2
    still, vector could provide an emplace_aggr, right? Commented Aug 14, 2017 at 19:38
  • @balki Right, there's no point taking c by && if nothing is done with its possible rvalueness; at the initialiser of the member, the argument is treated as an lvalue again, in the absence of a cast, so the member just gets copy-constructed. Even if the member was move-constructed, it's not idiomatic to require callers always to pass a temporary or std::move()d lvalue (though I will confess that I have a few corner-cases in my code where I do that, but only in implementation details). Commented Jun 25, 2018 at 19:27
  • 3
    Problem with this is that the struct then no longer is POD Commented Jun 8, 2021 at 18:13
62

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.

6
  • 1
    I don't see how this answers the question that was asked. Can you clarify?
    – MarkR
    Commented Jul 31, 2021 at 6:26
  • 1
    @MarkR struct T with fields a,b,c and no constructor is constructible as T{x,y,z} but not as T(x,y,z).
    – Nayuki
    Commented Oct 23, 2021 at 3:57
  • 6
    I am from the future and can confirm: This does work in GCC and MSVC. Not in Clang, though :( compiler-explorer.com/z/qGbGvsqzj
    – sebrockm
    Commented Apr 2, 2022 at 14:56
  • Sadly, a few years later, this has not changed in C++20 and still malfunctions the same way. Commented Jun 12, 2022 at 20:10
  • 4
    @AndrejPodzimek It changed in C++20. Just Clang hasn't implemented this yet. See on cppreference.
    – YurkoFlisk
    Commented Jul 27, 2022 at 21:29
30

Of course, this is not an answer, but it shows an interesting feature of tuples:

#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");
}
3
  • 12
    It certainly isn't an answer. what's so interesting about it? any type with a ctor can be emplaced in this way. tuple has a ctor. the op's struct didn't. that's the answer. Commented Jul 13, 2016 at 21:53
  • 11
    @underscore_d: I'm not sure that I remember every detail of what I was thinking 3½ years ago, but I thinj what I was suggesting was that if you just use a tuple instead of defining a POD struct, then you get a constructor for free, which means that you get the emplace syntax for free (among other things -- you also get lexicographic ordering). You lose member names, but sometimes it's less bother creating accessors than all the rest of the boilerplate you would otherwise need. I agree that Jerry Coffin's answer is much better than the accepted one. I also upvoted it years ago.
    – rici
    Commented Jul 13, 2016 at 22:56
  • 3
    Yeah, spelling it out helps me understand what you meant! Good point. I agree that sometimes the generalisation is bearable when weighed against the other stuff the STL provides for us: I use that semi-often with pair... but sometimes wonder if I really gain much in net terms, heh. But perhaps tuple will follow in the future. Thanks for expanding! Commented Jul 13, 2016 at 23:03
13

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 }; }
3
  • In your allocate function, don't you need to worry about alignment? See std::aligned_storage Commented Dec 13, 2012 at 0:26
  • No Problem. According to specification, effects of "void* ::operator new(size_t size)" are "The allocation function called by a new-expression to allocate size bytes of storage suitably aligned to represent any object of that size." Commented Dec 13, 2012 at 12:51
  • That code is horrifically ugly though. The whole point of the OP is "why doesnt this convenient feature work like I expect it to", nobody other than c++ philosophers wants to write code like this. Commented Nov 9, 2022 at 19:23
8

emplace_back returns a reference in c++17 and newer, so you can do this...

#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" };
}
2
  • 4
    Nice. But will it works as "emplace" or just a "push_back" with an rvalue? Commented Aug 9, 2023 at 21:35
  • 1
    @αλεχολυτ At first glance, it appears that the assignment operator will be invoked...
    – shycha
    Commented Sep 21, 2023 at 2:57
7

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.

-2

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...

5
  • A move constructor is automatically generated for T Commented Dec 11, 2012 at 2:39
  • 1
    @AndrewTomazos-Fathomling: only if no user ctors are defined
    – zaufi
    Commented Dec 11, 2012 at 2:41
  • 1
    Correct, and they are not. Commented Dec 11, 2012 at 2:46
  • @AndrewTomazos-Fathomling: but you have to define some, to avoid temporary instance on emplace_back() call :)
    – zaufi
    Commented Dec 11, 2012 at 2:48
  • 1
    Actually incorrect. A move constructor is automatically generated provided no destructor, copy constructor or assignment operators are defined. Defining a 3 argument memberwise constructor for use with emplace_back will not supress the default move constructor. Commented Dec 11, 2012 at 2:54
-3

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

V.push_back(std::move(T {42, 3.14, "foo"}));
2
  • 4
    You don't need to std::move() a temporary object T{...}. It's already a temporary object (rvalue). So you can just drop the std::move() from your example. Commented Jan 30, 2020 at 14:48
  • 1
    Moreover, even the type name T isn't necessary - the compiler can guess it. So just a "V.push_back{42, 3.14, "foo"}" will work. Commented Jan 30, 2020 at 14:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.