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

push_back<Args> and explicit constructors

44 views
Skip to first unread message

Magnus F

unread,
Nov 2, 2007, 11:34:25 AM11/2/07
to
If I read n2461 correctly then the following code:

struct Foo {
explicit Foo(int) { }
};

int main()
{
std::vector<Foo> fv;
fv.push_back(1);
}

is valid and would insert a Foo(1) instance in fv.

This would be a change from 14882:2003 where it was a compile error.
Is this an intended change or not?

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

mitsu...@gmail.com

unread,
Nov 7, 2007, 11:14:48 AM11/7/07
to
On 2 nov, 16:34, Magnus F <ma...@lysator.liu.se> wrote:
> If I read n2461 correctly then the following code:
>
> struct Foo {
> explicit Foo(int) { }
>
> };
>
> int main()
> {
> std::vector<Foo> fv;
> fv.push_back(1);
>
> }
>
> is valid and would insert a Foo(1) instance in fv.
>
> This would be a change from 14882:2003 where it was a compile error.
> Is this an intended change or not?
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-...@ncar.ucar.edu ]

> [ --- Please see the FAQ before posting. --- ]
> [ FAQ:http://www.comeaucomputing.com/csc/faq.html ]

The function calls an implicit conversion not an explicit conversion,
so the constructor signature does not match

Magnus F

unread,
Nov 7, 2007, 3:56:09 PM11/7/07
to

mitsuka...@gmail.com wrote:
> On 2 nov, 16:34, Magnus F <ma...@lysator.liu.se> wrote:
> > If I read n2461 correctly then the following code:
> >
> > struct Foo {
> > explicit Foo(int) { }
> > };
> >
> > int main()
> > {
> > std::vector<Foo> fv;
> > fv.push_back(1);
> > }
> >
> > is valid and would insert a Foo(1) instance in fv.
> >
> > This would be a change from 14882:2003 where it was a compile error.
> > Is this an intended change or not?
>
> The function calls an implicit conversion not an explicit conversion,
> so the constructor signature does not match
>

Now you lost me, where is the converting constructor from int to Foo?

The problem I see is that in the 1998 standard push_back is declared
as

void vector<T, ...>::push_back(const T&)

while it, in n2461, is declared as

template <class... Args> void push_back(Args&&... args);

Now consider push_back(1), in the 1998 version the conversion sequence
is

int is _implicitly_ converted to a Foo that is inserted, but since
Foo(int) is explicit there is no valid conversion sequence so the code
is invalid.

in the n2461 version the conversion sequence is

int is handed to push_back, push_back(args) is equivalent to
a.emplace(a.end(), std::forward<Args>(args)...), that is
a.emplace(a.end(), 1), that in turn inserts an T(args), in this case
an Foo(1) and the big difference is that now Foo(1) is _explicitly_
called.

I want to make sure that the call to Foo(1) continues to be implicitly
called so that my example fails to compile in the future as well as we
would be degrading the value of explicit constructors otherwise.

On a similar note I would prefer if emplace had the same semantics so
that fv.emplace(fv.end(), 1) also gave an compile error since there is
no implicit conversion from int to Foo.

Greg Herlihy

unread,
Nov 12, 2007, 2:31:12 AM11/12/07
to
On Nov 7, 12:56 pm, Magnus F <ma...@lysator.liu.se> wrote:
>
> The problem I see is that in the 1998 standard push_back is declared
> as
>
> void vector<T, ...>::push_back(const T&)
>
> while it, in n2461, is declared as
>
> template <class... Args> void push_back(Args&&... args);
>
> Now consider push_back(1), in the 1998 version the conversion sequence
> is
>
> int is _implicitly_ converted to a Foo that is inserted, but since
> Foo(int) is explicit there is no valid conversion sequence so the code
> is invalid.
>
> in the n2461 version the conversion sequence is
>
> int is handed to push_back, push_back(args) is equivalent to
> a.emplace(a.end(), std::forward<Args>(args)...), that is
> a.emplace(a.end(), 1), that in turn inserts an T(args), in this case
> an Foo(1) and the big difference is that now Foo(1) is _explicitly_
> called.

Yes, clearly Foo's constructor must be explicitly called in order to
construct the object (being added to the container) from whatever
arguments are provided. Otherwise, a C++09 program like the one below
- would not compile:

struct A
{
A() {}
A(int, int) {}
};

int main()
{
std::vector<A> v;

v.push_back(); // adds A() to v
v.push_back(1, 2); // adds A(1,2) to v
}

> I want to make sure that the call to Foo(1) continues to be implicitly
> called so that my example fails to compile in the future as well as we
> would be degrading the value of explicit constructors otherwise.

Nothing about explicit constructors has changed. Only the containers'
declaration of puah_back() has changed - and been made more useful.
With the new interface, objects can now be added to a container by
constructing them "in place" - without any copying involved at all.

Moreover, the conversion from an int to a Foo when adding to the
container is hardly "implicit". There is no doubt which push_back()
method is being called here. Nor is the programmer in all likelihood
expecting an int to reside in a container of Foo's. So the only
plausible meaning of the push_back() call - is to construct a Foo from
the int argument provided.

In fact, it is important to view the situation the other way around.
Say a C++ programmer has an int - and a container of Foo's. Now, the
programmer wants to use the int to construct a Foo and add the newly-
constructed Foo to the end of the container. Assuming the programmer
wants to accomplish this task in the most efficient way possible - why
should an explicit constructor get in the way? If the explicit
constructor did get in the way - then the programmer would be faced
with a choice between safety and efficiency - a choice that should not
have to be made.

Lastly, this change breaks no existing C++ programs. True, code that
would not have compiled in the past will now compile in C++09, due to
this change. But the same could be said of any other extension to C++
- and therefore does not present much of an argument against making
the change.

Greg

Magnus F

unread,
Nov 13, 2007, 6:29:24 PM11/13/07
to
Greg Herlihy wrote:
>
> Yes, clearly Foo's constructor must be explicitly called in order to
> construct the object (being added to the container) from whatever
> arguments are provided. Otherwise, a C++09 program like the one below
> - would not compile:
>
> struct A
> {
> A() {}
> A(int, int) {}
> };
>
> int main()
> {
> std::vector<A> v;
>
> v.push_back(); // adds A() to v
> v.push_back(1, 2); // adds A(1,2) to v
> }

I agree with this.

> > I want to make sure that the call to Foo(1) continues to be implicitly
> > called so that my example fails to compile in the future as well as we
> > would be degrading the value of explicit constructors otherwise.
>
> Nothing about explicit constructors has changed. Only the containers'
> declaration of puah_back() has changed - and been made more useful.
> With the new interface, objects can now be added to a container by
> constructing them "in place" - without any copying involved at all.

This is good, I really want to keep that cake as well.

> Moreover, the conversion from an int to a Foo when adding to the
> container is hardly "implicit". There is no doubt which push_back()
> method is being called here. Nor is the programmer in all likelihood
> expecting an int to reside in a container of Foo's. So the only
> plausible meaning of the push_back() call - is to construct a Foo from
> the int argument provided.

What I am saying is that maybe there should be three push_back
methods,

puch_back(const T& t) { /* same as 1998 */ }
push_back() { push_back(T()); }
push_back(U u, V v, args...) { push_back(T(u, v, args...)); }

but as you have stated above it would be nice to be able to keep the
in-place construction.

Additionally it is allowed to have any constructor, not just one
argument ones, marked explicit so the best would be if there was a way
to say (non-explicit T)(args...).

If we disregard argument packs then this can be done using

static_cast<T>(value)

so if static_cast is extended to handle argument packs then both of us
could get the result we are asking for using

push_back(Args... args) { something(T(static_cast<T>(args...)); }

> In fact, it is important to view the situation the other way around.
> Say a C++ programmer has an int - and a container of Foo's. Now, the
> programmer wants to use the int to construct a Foo and add the newly-
> constructed Foo to the end of the container. Assuming the programmer
> wants to accomplish this task in the most efficient way possible - why
> should an explicit constructor get in the way? If the explicit
> constructor did get in the way - then the programmer would be faced
> with a choice between safety and efficiency - a choice that should not
> have to be made.

I agree. I also think the static_cast idea above handles that case in
a nice way except that it is very late in the game for changes to the
language.

The big question here is not if the push_back extension is good but
what the meaning of explicit is.

> Lastly, this change breaks no existing C++ programs. True, code that
> would not have compiled in the past will now compile in C++09, due to
> this change. But the same could be said of any other extension to C++
> - and therefore does not present much of an argument against making
> the change.

I think this statement is wrong. Consider

struct S {
explicit S(long) { }
S(short) { }
};

int main()
{
std::vector<S> vs;
vs.push_back(1);
}

which is valid C++98 but ambigous using C++0x

/MF

Magnus F

unread,
Nov 14, 2007, 4:27:13 PM11/14/07
to

Magnus F wrote:
>
> but as you have stated above it would be nice to be able to keep the
> in-place construction.
>
> Additionally it is allowed to have any constructor, not just one
> argument ones, marked explicit so the best would be if there was a way
> to say (non-explicit T)(args...).
>
> If we disregard argument packs then this can be done using
>
> static_cast<T>(value)

Here I was wrong, static_cast do call explicit constructors.
That said, I still think it would be a good thing to have a way to
make an explicit constructor call that only allows non-explicit
constructors in the overload set. Maybe something like
weak_cast<T>(arg-list)

0 new messages