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

C++StlVectorC++11Backwards Compatibility

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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

Solution 2 - C++

I think solution for use-case you described is not optimal and not complete, that's why you got problems upgrading to C++11.

C++ always cares about semantic and when you write program in c++ you'd better to understand your semantic. So in your case you wish to create N objects, but while you are not changing them you wish them to share same memory for optimization. Nice idea, but how to get this done:

  1. copy constructor.
  2. static implementation + copy constructor. Have you considered both solutions?

Consider you need M vectors of N objects, how many times shared memory will be allocated if you choose 1st scenario? It is M, but why do we need to allocate memory M times if we want to create vectors containing MxN objects?

So correct implementation here is to point to static memory by default, and allocate memory only if object is changed. In such a case allocating M vectors of N objects will give you... 1 'shared' memory allocation.

In your case you violated correct semantic abusing copy constructor, which is:

  1. not obvious
  2. not optimal and now you have to pay off.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexandre C.View Question on Stackoverflow
Solution 1 - C++James McNellisView Answer on Stackoverflow
Solution 2 - C++Alexey SergeevView Answer on Stackoverflow