Search code examples
c++stlvectorc++11backwards-compatibility

std::vector, default construction, C++11 and breaking changes


I ran today against a quite subtle issue I'd like to have your opinion on.

Consider the following garden-variety shared-body-idiom class:

struct S
{
    S() : p_impl(new impl) {}
private:
    struct impl;
    boost::shared_ptr<impl> p_impl;
};

The fun appears when you try to put those into vectors in the following way:

std::vector<S> v(42);

Now, with MSVC 8 at least, all the elements in v share the same impl member. Actually, what causes this is the vector constructor:

template <typename T, typename A = ...>
class vector
{
    vector(size_t n, const T& x = T(), const A& a = A());
    ...
};

Under the scenes, only one S object gets default constructed, the n elements of the vector are copied from it.

Now, with C++11, there are rvalue references. So it cannot work like this. If a vector is constructed as

std::vector<S> v(42);

then most likely, implementations will chose to default construct the n objects inside the vector, since copy construction may not be available. This would be a breaking change in this case.

My question is:

  1. Does the C++03 standard mandates that std::vector must have a constructor defined as above, ie. with a default argument ? In particular is there a guarantee that the entries of the vector object get copied instead of default constructed ?
  2. What does the C++11 standard say about this same point ?
  3. I see this as a possibility for a breaking change between C++03 and C+11. Has this issue been investigated ? Solved ?

PS: Please no comments about the default constructor of the class S above. It was this or implementing some form of lazy construction.


Solution

  • Does the C++03 standard mandate that std::vector must have a constructor defined as above, i.e. with a default argument? In particular is there a guarantee that the entries of the vector object get copied instead of default constructed?

    Yes, the specified behavior is that x is copied n times so that the container is initialized to contain with n elements that are all copies of x.


    What does the C++11 Standard say about this same point?

    In C++11 this constructor has been turned into two constructors.

    vector(size_type n, const T& x, const Allocator& = Allocator()); // (1)
    explicit vector(size_type n);                                    // (2)
    

    Except for the fact that it no longer has a default argument for the second parameter, (1) works the same way as it does in C++03: x is copied n times.

    In lieu of the default argument for x, (2) has been added. This constructor value-initializes n elements in the container. No copies are made.

    If you require the old behavior, you can ensure that (1) is called by providing a second argument to the constructor invocation:

    std::vector<S> v(42, S());
    

    I see this as a possibility for a breaking change between C++03 and C++11. I see this as a possibility for a breaking change between C++03 and C++11. Has this issue been investigated? Solved?

    Yes, as your example demonstrates, this is indeed a breaking change.

    As I am not a member of the C++ standardization committee (and I haven't paid particularly close attention to library-related papers in the mailings), I don't know to what degree this breaking change was discussed.