Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Push_back works, but emplace_back doesn't, when constructing with initialiser list

948 views
Skip to first unread message

Vir Campestris

unread,
Nov 28, 2017, 4:26:01 PM11/28/17
to
This code fragment
{
struct s { int a; int b;};
std::vector<s> foo;
foo.push_back({1,2});
foo.emplace_back({3,4});
}
when built with GCC, the push_back line works fine, but the emplace_back
one doesn't. WTH?

Andy

Öö Tiib

unread,
Nov 28, 2017, 5:09:27 PM11/28/17
to
Not sure what you ask. Can you tell what you expected and why?
The emplace_back accepts and forwards constructor arguments of s.
The push_back accepts rvalue reference to s or reference to const s.

There are 3 constructors of your s so those work ...

foo.emplace_back(); // no arguments - default constructor
s const kaka{};
foo.emplace_back(kaka); // reference to const s - copy constructor
foo.emplace_back(s{3,4}); // rvalue reference to s - move constructor

Those however don't work ...

foo.emplace_back({3,4}); // std::initializer_list<int> - WTF?
foo.emplace_back(3,4); // two ints - WTF?
foo.emplace_back(foo); // std::vector<s> - WTF?

Juha Nieminen

unread,
Dec 1, 2017, 1:47:30 AM12/1/17
to
They are not equivalent.

foo.push_back({something}) is equivalent to to foo.push_back(s{something})
(because push_back() takes a parameter of type s by reference). You are
effectively constructing an object of type s first, and then passing that
object to push_back().

However, foo.emplace_back(something) will take the parameter as-is, and
try to forward it to the constructor of an object of type s. In this case
{3,4} is of type std::initializer_list, and s has no constructor that
takes such a thing.

This ought to work (for a similar reason why the push_back() version does):
foo.emplace_back(s{3,4});

Vir Campestris

unread,
Dec 1, 2017, 4:53:44 PM12/1/17
to
On 01/12/2017 06:47, Juha Nieminen wrote:
> They are not equivalent.
>
> foo.push_back({something}) is equivalent to to foo.push_back(s{something})
> (because push_back() takes a parameter of type s by reference). You are
> effectively constructing an object of type s first, and then passing that
> object to push_back().
>
> However, foo.emplace_back(something) will take the parameter as-is, and
> try to forward it to the constructor of an object of type s. In this case
> {3,4} is of type std::initializer_list, and s has no constructor that
> takes such a thing.
>
> This ought to work (for a similar reason why the push_back() version does):
> foo.emplace_back(s{3,4});

Right. That makes perfect sense.

Or does it?

In the case foo.push_back(s{something}) what (default) constructor of s
is being called? Isn't {3,4} an initialiser list there too?

Andy

Chris Vine

unread,
Dec 1, 2017, 5:07:42 PM12/1/17
to
The initialisation of the temporary in 'foo.push_back(s{3,4})' and
'foo.emplace_back(s{3,4})' have effect as a call to a constructor
taking two arguments if 's' has no initialiser list constructor. Why
does this not occur with foo.emplace_back({3,4})? Probably because
emplace_back is function templated on its arguments and this is an
artefact of the template deduction rules: once deduced as an
initialiser list, always deduced as an initialiser list.

Chris Vine

unread,
Dec 1, 2017, 5:20:38 PM12/1/17
to
On Fri, 1 Dec 2017 22:07:26 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> On Fri, 1 Dec 2017 21:53:25 +0000
> Vir Campestris <vir.cam...@invalid.invalid> wrote:
> > On 01/12/2017 06:47, Juha Nieminen wrote:
> > > They are not equivalent.
> > >
> > > foo.push_back({something}) is equivalent to to
> > > foo.push_back(s{something}) (because push_back() takes a parameter
> > > of type s by reference). You are effectively constructing an
> > > object of type s first, and then passing that object to
> > > push_back().
> > >
> > > However, foo.emplace_back(something) will take the parameter
> > > as-is, and try to forward it to the constructor of an object of
> > > type s. In this case {3,4} is of type std::initializer_list, and
> > > s has no constructor that takes such a thing.
> > >
> > > This ought to work (for a similar reason why the push_back()
> > > version does): foo.emplace_back(s{3,4});
> >
> > Right. That makes perfect sense.
> >
> > Or does it?
> >
> > In the case foo.push_back(s{something}) what (default) constructor
> > of s is being called? Isn't {3,4} an initialiser list there too?
>
> The initialisation of the temporary in 'foo.push_back(s{3,4})' and
> 'foo.emplace_back(s{3,4})' have effect as a call to a constructor
> taking two arguments if 's' has no initialiser list constructor.

Corrigendum: Or in this case, as an aggregate initialisation, as there
is no explicit constructor.

Vir Campestris

unread,
Dec 1, 2017, 6:02:43 PM12/1/17
to
On 01/12/2017 22:20, Chris Vine wrote:
> Corrigendum: Or in this case, as an aggregate initialisation, as there
> is no explicit constructor.

Ah! That'll be it.

Thanks to you and Juha.

Andy

Juha Nieminen

unread,
Dec 4, 2017, 3:06:22 AM12/4/17
to
Vir Campestris <vir.cam...@invalid.invalid> wrote:
> In the case foo.push_back(s{something}) what (default) constructor of s
> is being called? Isn't {3,4} an initialiser list there too?

s{3,4} is in this case an aggregate initialization, not an initializer_list.
They use the exact same syntax, on purpose. Admittedly it can be a bit
confusing sometimes. (They use the exact same syntax so that the user can
"override" the aggregate initialization with a custom constructor.)
0 new messages