Topic: push_back<Args> and explicit constructors [Defect in the working


Author: Magnus F <magfr@lysator.liu.se>
Date: Tue, 13 Nov 2007 17:29:24 CST
Raw View
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

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