std::vector must have strong move guarantees.

1,996 views
Skip to first unread message

t...@gmail.com

unread,
Oct 30, 2017, 12:49:35 PM10/30/17
to ISO C++ Standard - Future Proposals
Now std::vector, does not have any guarantees about using move semantic during objects reallocation (like on vector grow).
The only "guarantee" that we have now, is that it allowed to move elements under certain circumstances (e.g.: noexcept move constructor + noexcept destructor).

I think, we need to have standard defined guarantees, when std::vector MUST use move semantic, instead of copy.
Besides performance guarantees, this will allow us to build logic based on knowledge that we work with the same moved object [in the cases, when object can be both moved and copied].

It seems that all mainstream compilers already use move semantic in "noexcept move constructor + noexcept destructor" cases, but I do believe, we must force this behavior.

Vishal Oza

unread,
Oct 30, 2017, 1:03:30 PM10/30/17
to ISO C++ Standard - Future Proposals
I agree but I think this is an issue for standard library implimentor and not compiler writing. The only issue with this change is support for pre-C++ 11 code before move semantics was define. The other issue is which move should be used?

Nevin Liber

unread,
Oct 30, 2017, 1:09:51 PM10/30/17
to std-pr...@isocpp.org
On Mon, Oct 30, 2017 at 11:49 AM, t...@gmail.com <towe...@gmail.com> wrote:
Now std::vector, does not have any guarantees about using move semantic during objects reallocation (like on vector grow).

Really?

Take the wording in [vector.modifiers] for insert/emplace_back/emplace/push_back:

If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects.
 
If an exception is thrown while inserting a single element at the end and T is CopyInsertableor is_­nothrow_­move_­constructible_­v<T> is true, there are no effects.


Sure looks like a guarantee to me.


Where exactly is this kind of wording missing?
 -- 
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com>  +1-847-691-1404

towe...@gmail.com

unread,
Oct 30, 2017, 1:35:23 PM10/30/17
to ISO C++ Standard - Future Proposals
If you talking about section "23.3.6.5 vector modifiers" at page 772 of n3242:

What exactly that wording guarantee? In English.
Does not have side effects = allowed to omit. So what?

That particular section contains not a word about how actually reallocation should happened.


понедельник, 30 октября 2017 г., 19:09:51 UTC+2 пользователь Nevin ":-)" Liber написал:

towe...@gmail.com

unread,
Oct 30, 2017, 1:47:30 PM10/30/17
to ISO C++ Standard - Future Proposals
понедельник, 30 октября 2017 г., 19:03:30 UTC+2 пользователь Vishal Oza написал:
I agree but I think this is an issue for standard library implimentor and not compiler writing. The only issue with this change is support for pre-C++ 11 code before move semantics was define. The other issue is which move should be used?

standard library behavior described in section 17 of "Standard for Programming Language C++" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf (in the same document with compiler).

This is obviously "issue" for standard library. I'm talking about to force implementors to use move semantic, by adding/clarifying wording to use move semantics instead of copy during reallocation (where applicable).

Matt Calabrese

unread,
Oct 30, 2017, 1:52:45 PM10/30/17
to ISO C++ Standard - Future Proposals
On Mon, Oct 30, 2017 at 1:09 PM, Nevin Liber <ne...@eviloverlord.com> wrote:
If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects.
 
If an exception is thrown while inserting a single element at the end and T is CopyInsertableor is_­nothrow_­move_­constructible_­v<T> is true, there are no effects.


Sure looks like a guarantee to me.

Where exactly is this kind of wording missing?

I think the kind of situation that he may be talking about is the following:

//////////
std::vector<std::vector<int>> foo(1);
foo.front().push_back(5);
auto it = foo.front().begin();
foo.resize(20);  // Should this be guaranteed to not invalidate "it"?
//////////

towe...@gmail.com

unread,
Oct 30, 2017, 2:11:41 PM10/30/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
I'm talking about the following situation:

struct MyData{
    int x;
    MyData(){}
    MyData(int x) : x(x) {}

    // vector move friendly
    MyData(MyData&&) noexcept {}
    ~MyData() noexcept {}

   // but, also have copy constructor
    MyData(const MyData&) noexcept {}
};

std::vector<MyData> vec;

vec.emplace_back(1);
vec.emplace_back(2);

// here vector must reallocate 1,2 to newly allocated memory chunk.
// Will it move [1,2] or copy? Now it is up to std implementer.
// I want move [1,2] by standard.
vec.emplace_back(3);


Here is discussion of similar problem:
https://stackoverflow.com/questions/8001823/how-to-enforce-move-semantics-when-a-vector-grows



понедельник, 30 октября 2017 г., 18:49:35 UTC+2 пользователь t...@gmail.com написал:

Jonathan Müller

unread,
Oct 30, 2017, 2:26:13 PM10/30/17
to std-pr...@isocpp.org
On 30.10.2017 19:11, towe...@gmail.com wrote:
> I'm talking about the following situation:
>
> struct MyData{
>     int x;
>     MyData(){}
>     MyData(int x) : x(x) {}
>
>     // vector move friendly
>     MyData(MyData&&) noexcept {}
>     ~MyData() noexcept {}
>
>    // but, also have copy constructor
>     MyData(const MyData&) noexcept {}
> };
>
> std::vector<MyData> vec;
>
> vec.emplace_back(1);
> vec.emplace_back(2);
>
> // here vector must reallocate 1,2 to newly allocated memory chunk.
> // Will it move [1,2] or copy? Now it is up to std implementer.
> // I want move [1,2] by standard.
> vec.emplace_back(3);
>
>
Why do you want it enforced?

towe...@gmail.com

unread,
Oct 30, 2017, 2:44:41 PM10/30/17
to ISO C++ Standard - Future Proposals
Well...

At first, this will have stronger performances guarantees. Example: std::vector<std::shared_ptr> - obviously is MUCH cheaper to move, then copy...

At second, vector will contain the same object (moved a few times of course, but) even after reallocation. Knowledge that we work with the same object can be exploited. For example, I use move constructor / assignment to track object address https://github.com/tower120/trackable_ptr . Move implies that we still use the same object, copy - that we have another object. I'm sure there can be other examples, but can't figure out right now...

Move is move - if we reallocate objects, it is logically correct to move them. If we copy - we copy.


понедельник, 30 октября 2017 г., 20:26:13 UTC+2 пользователь Jonathan Müller написал:

Jonathan Müller

unread,
Oct 30, 2017, 2:49:12 PM10/30/17
to std-pr...@isocpp.org
On 30.10.2017 19:44, towe...@gmail.com wrote:
> Well...
>
> At first, this will have stronger performances guarantees. Example:
> std::vector<std::shared_ptr> - obviously is MUCH cheaper to move, then
> copy...

Yes, but that's a QoI thing.
We also don't need to mandate that you must not put sleep into push_back
(I'm exaggerating).

>
> At second, vector will contain the same object (moved a few times of
> course, but) even after reallocation. Knowledge that we work with the
> same object can be exploited. For example, I use move constructor /
> assignment to track object address
> https://github.com/tower120/trackable_ptr . Move implies that we still
> use the same object, copy - that we have another object. I'm sure there
> can be other examples, but can't figure out right now...
>

See, I don't think you should do that.

A move should be indistinguishable from a copy - if you don't look at
the old object, it is just a faster copy, after all.
And I'm not sure what you mean by "move implies that we still use the
same object": we're not, we're using a *copy* of that object. The copy
is created by moving but that's not really important.

> Move is move - if we reallocate objects, it is logically correct to move
> them. If we copy - we copy.
>

We should move, but which (current) implementation doesn't do move?

towe...@gmail.com

unread,
Oct 30, 2017, 3:06:56 PM10/30/17
to ISO C++ Standard - Future Proposals
See, I don't think you should do that.

A move should be indistinguishable from a copy - if you don't look at
the old object, it is just a faster copy, after all.

So, move = faster copy ... Is this defined somewhere in standard? I thought about move semantic rather as Rust's move - we work with the same object, but now just move to another space, faster/slower is just optimization details.


We should move, but which (current) implementation doesn't do move?
 
All  mainstream compilers do. The question is - should we rely on compilers good will? :)

Thiago Macieira

unread,
Oct 30, 2017, 3:19:54 PM10/30/17
to std-pr...@isocpp.org
On segunda-feira, 30 de outubro de 2017 12:06:56 PDT towe...@gmail.com wrote:
> So, move = faster copy ... Is this defined somewhere in standard? I thought
> about move semantic rather as Rust's move - we work with the same object,
> but now just move to another space, faster/slower is just optimization
> details.

This discussion is getting blocked by the identity of an object. If an object
is the same object if and only if it has the same pointer address, then
std::vector always copies when it reallocates. Whether it calls the move
constructor or not is a detail.

If an object is the same provided it was always moved from another, then
std::vector could still have the same original object. This is of relevance
for objects whose copy constructor needs to allocate memory (like pimpl
objects), in which case you can tell whether it's the same original object by
looking at those pointers.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Nevin Liber

unread,
Oct 30, 2017, 3:31:24 PM10/30/17
to std-pr...@isocpp.org
On Mon, Oct 30, 2017 at 2:06 PM, <towe...@gmail.com> wrote:
See, I don't think you should do that.

A move should be indistinguishable from a copy - if you don't look at
the old object, it is just a faster copy, after all.

So, move = faster copy

Not always.  Move may have to do more work, as in:

template<typename T, T v = T{}>

struct MoveIt

{

    MoveIt() : t{v} {}

    MoveIt(T t_) : t{std::move(t_)} {}

    operator T&() { return t; }

    operator T const&() const { return t; }


    MoveIt(MoveIt const&) = default;

    MoveIt(MoveIt&& that) : MoveIt{static_cast<MoveIt const&>(that)} { that.t = v; }


    MoveIt& operator=(MoveIt const&) = default;

    MoveIt& operator=(MoveIt&& that) { *this = static_cast<MoveIt const&>(that); that.t = v; return *this; }


private:

    T t;

};


towe...@gmail.com

unread,
Oct 30, 2017, 3:43:23 PM10/30/17
to ISO C++ Standard - Future Proposals
 
If an object is the same object if and only if it has the same pointer address, then
std::vector always copies when it reallocates.

What "same object"? About which two objects you talking about? About object in chunk of memory moved from (SOURCE) and [not yet constructed] object in chunk of memory moved to (DESTINATION) ? How they possibly can have the same address, then?

> then std::vector always copies when it reallocates

It ALWAYS reallocates physically, yes (in terms do some bits copy). But logically we can MOVE object (you still have one object - the same), or COPY (now you have two obejcts). Move semantics should imply logical move (like in std::unique_ptr, std::unique_lock), copy semantic - copy (well, just value copy).

towe...@gmail.com

unread,
Oct 30, 2017, 3:47:41 PM10/30/17
to ISO C++ Standard - Future Proposals
See! Exactly what I'm talking about! Move for move, copy for copy. Have nothing to do with amount of work. It's all about logical distinction. Though logically move should have less amount of work, then copy :)

понедельник, 30 октября 2017 г., 21:31:24 UTC+2 пользователь Nevin ":-)" Liber написал:

Jonathan Müller

unread,
Oct 30, 2017, 3:52:33 PM10/30/17
to std-pr...@isocpp.org
On 30.10.2017 20:06, towe...@gmail.com wrote:
> See, I don't think you should do that.
>
> A move should be indistinguishable from a copy - if you don't look at
> the old object, it is just a faster copy, after all.
>
>
> So, move = faster copy ... Is this defined somewhere in standard? I
> thought about move semantic rather as Rust's move - we work with the
> same object, but now just move to another space, faster/slower is just
> optimization details.

Look for "regular types" for example.
And no, C++ move semantics are *not* Rust move semantics - we work with
a *different* object, a copy. The old object still exists and can be used.

As others have pointed out, move is not necessarily faster, yes, but
that's its motivation.

>
> We should move, but which (current) implementation doesn't do move?
>
> All  mainstream compilers do. The question is - should we rely on
> compilers good will? :)

But we are also relying on compilers and library implementers good will
already - see my contrived sleep() example.

towe...@gmail.com

unread,
Oct 30, 2017, 4:04:33 PM10/30/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
Alright people, you convinced me.
Move = just fast copy, and we should rely on compilers.
Suggestion is closed.

Thiago Macieira

unread,
Oct 30, 2017, 6:53:05 PM10/30/17
to std-pr...@isocpp.org
On segunda-feira, 30 de outubro de 2017 12:43:23 PDT towe...@gmail.com wrote:
> It ALWAYS reallocates physically, yes (in terms do some bits copy). But
> logically we can MOVE object (you still have one object - the same), or
> COPY (now you have two obejcts). Move semantics should imply logical move
> (like in std::unique_ptr, std::unique_lock), copy semantic - copy (well,
> just value copy).

That's exactly the issue: the semantic. Is the the destination object after
the move the same object or not? How about a swap?

The language does not specify what it is. So please be clear in what you
consider "the same". Or, better yet, don't use the term at all. Explain your
request and use-case without that.

Thiago Macieira

unread,
Oct 30, 2017, 6:57:36 PM10/30/17
to std-pr...@isocpp.org
There's still a merit to your suggestion. But you have to be clear on why it
should be mandated.

The standard already mandates that the move should happen if:
- the type can't be copied (deleted copy constructor)
- the move constructor is noexcept but the copy one may throw

You'd like to enforce that if both are available and both won't throw (or both
will), that the move constructor be called.

I think that's a reasonable ask. You just have to write good examples of which
conditions would benefit from this. One of them is that copy constructors
usually have more work to do. Just come up with a couple more cases.

Nevin Liber

unread,
Oct 30, 2017, 7:06:18 PM10/30/17
to std-pr...@isocpp.org
On Mon, Oct 30, 2017 at 5:57 PM, Thiago Macieira <thi...@macieira.org> wrote:
I think that's a reasonable ask. You just have to write good examples of which
conditions would benefit from this. One of them is that copy constructors
usually have more work to do.

I don't think that holds in general if both the copy constructor and move constructor are noexcept.  The shared_ptr case is an anomaly (but an important one) in that the copy constructor has less but slower work to do (copy pointers and increase an atomic refCount), while the move constructor has more but faster work to do (copy pointers in this and clear pointers in the source), because it has to modify two objects.

Nicol Bolas

unread,
Oct 30, 2017, 9:54:10 PM10/30/17
to ISO C++ Standard - Future Proposals
Earlier in the thread, someone posted a class template that had a faster noexcept copy than move. Like shared_ptr, it effectively dealt with augmenting an existing value with additional "tracking" information. Unlike shared_ptr, it implemented odd-ball value semantics with it.

Normally, with a value semantics type, if you copy it, the two objects are distinct but have the same value. In this case, it added tracking information that could distinguish between the two objects. So even if they conceptually had the same "value", they didn't necessarily have the same tracking information.

But moving such an object did not. As such, you were permitted to track where the guts of the value came from, but only via move, not via copy.

I would say that such a type is decidedly strange, semantics-wise. Not unreasonable, just unexpected.

At the end of the day, the questions I think need to be asked are:

1. Is leaving this implementation-defined a good thing?
2. Is there a right answer that will always result in improved performance?
3. Is leaving this implementation-defined making it difficult for users to rely on `vector`'s behavior, one way or the other?

#3 is important. Consider that object type that I spoke of, which had different copy and move behavior. Whether a `vector` of such objects moves or copies its contents is probably really important to the person writing that type. Will this force them to not declare their copy constructor `noexcept`, simply to force a specific behavior?

It's not so much that a user will prefer copy or move; it's that they need to rely on it always being one or the other. That is, for any given `vector<T>`, users should be able to know that reallocation will invoke a copy or a move. Which one it ultimately is isn't as important as picking an answer and forcing implementations to implement it.

towe...@gmail.com

unread,
Oct 30, 2017, 9:56:51 PM10/30/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
Okay, I think I start to understand your motivation. You think, that having both constructors available to use during reallocation phase will theoretically allow compiler to choose faster path? Sounds very vague... What motivation, then,  to write move constructor in class, if it is slower beforehand?

Other examples with cases were both the copy constructor and move constructor are noexcept, are:
std::vector<
std::vector<T>>
std::vector<std::string>
std::vector<std::function>

If you need examples, where compiler will not be able to determine which constructor is faster at compile time, consider this:

struct Data{
    Data(){}

    std::function<void()> copy_ctr_hook;
    std::function<void()> move_ctr_hook;

    Data(const Data& other) noexcept {
        other.copy_ctr_hook();
    }
    Data(Data&& other) noexcept {
        other.move_ctr_hook();
    }

    ~Data() noexcept {}
}

I want to see how compiler will decide which constructor is faster. Kinda hypothetical, but if your main argument, that "compiler knows better"...

Now, you have to brawl with compiler/std library to get required behavior (aka force compiler to use your move constructor). Because compiler allowed NOT TO use it, you have no idea what exactly will happened if you change compiler/platform.

Nicol Bolas

unread,
Oct 30, 2017, 10:21:06 PM10/30/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com


On Monday, October 30, 2017 at 9:56:51 PM UTC-4, towe...@gmail.com wrote:
Okay, I think I start to understand your motivation. You think, that having both constructors available to use during reallocation phase will theoretically allow compiler to choose faster path? Sounds very vague... What motivation, then,  to write move constructor in class, if it is slower beforehand?

Other examples with cases were both the copy constructor and move constructor are noexcept, are:
std::vector<
std::vector<T>>
std::vector<std::string>
std::vector<std::function>

Not one of the types in those `vector`s has a `noexcept` copy constructor. `std::allocator` can throw, and `std::function`'s copy constructor is not `noexcept`. Indeed, `function`'s move constructor isn't even required to be `noexcept`.


Thiago Macieira

unread,
Oct 31, 2017, 12:25:15 AM10/31/17
to std-pr...@isocpp.org
On segunda-feira, 30 de outubro de 2017 16:05:34 PDT Nevin Liber wrote:
> I don't think that holds in general if both the copy constructor and move
> constructor are noexcept. The shared_ptr case is an anomaly (but an
> important one) in that the copy constructor has less but slower work to do
> (copy pointers and increase an *atomic* refCount), while the move
> constructor has more but faster work to do (copy pointers in this and clear
> pointers in the source), because it has to modify two objects.

My gut feeling is that noexcept move constructors do just a little less work
then their equivalent noexcept copy constructor. So enforcing a call to the
move constructor is probably a gain, but not by much.

That's why I suggested coming up with more scenarios where this may be
beneficial.

Nevin Liber

unread,
Oct 31, 2017, 1:25:32 AM10/31/17
to std-pr...@isocpp.org
On Mon, Oct 30, 2017 at 11:25 PM, Thiago Macieira <thi...@macieira.org> wrote:
On segunda-feira, 30 de outubro de 2017 16:05:34 PDT Nevin Liber wrote:
> I don't think that holds in general if both the copy constructor and move
> constructor are noexcept.  The shared_ptr case is an anomaly (but an
> important one) in that the copy constructor has less but slower work to do
> (copy pointers and increase an *atomic* refCount), while the move
> constructor has more but faster work to do (copy pointers in this and clear
> pointers in the source), because it has to modify two objects.

My gut feeling is that noexcept move constructors do just a little less work
then their equivalent noexcept copy constructor.

My gut feeling is that noexcept copy constructors are rare.

The biggest cause of them is fundamental types, whose move operation does the same thing as the copy operation.

Next comes legacy code, which declares copy constructors but not move constructors.  Those also do the exact same amount of work for both moving and copying.

Next comes helper types like my MoveIt example, so aggregates don't have to define their own move constructors.  Those do more work in their move constructors than in their copy constructors.  Now, because they are just helper types, a vector of them would be very unlikely in practice.

That leaves, well, things like shared_ptr, which is an anomaly because of the atomic refCount, which is an expensive to increment resource that cannot fail.

Of course, you can have aggregates of all these types, but then performance depends on the mix.

People can, of course, write their own move constructors, but unless copying involves a relatively expensive resource that cannot fail (like the atomic refCount in shared_ptr), it'll be either the same as or more expensive than copying.
 
So enforcing a call to the
move constructor is probably a gain, but not by much.

I think it is a gain because of shared_ptr and aggregates which contain shared_ptr like types, but I'm not convinced that shouldn't be left to QoI.

Nicol Bolas

unread,
Oct 31, 2017, 2:13:22 AM10/31/17
to ISO C++ Standard - Future Proposals
I'm not convinced that observable behavior of this sort should be left to QoI.

It's not like we allow `std::sort` to arbitrarily decide whether it's going to copy or move the objects it gets. It will invoke move constructors/assignment if the object has them, and invoke copy constructors/assignments if it does not. Guaranteed (as I understand it). We don't leave that to "quality" of implementation.

So why should `vector`'s reallocation behavior not have a similar guarantee? Obviously not exactly the same one (pick the noexcept move over copy if its available), but why should there be any implementation variance here?

This is observable behavior. What do we gain by making it unpredictable? I say that, unless someone can demonstrate how an implementation can improve its quality based on such wiggle room, we should eliminate it.

towe...@gmail.com

unread,
Oct 31, 2017, 11:16:53 AM10/31/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
I am sorry to ask, but you all speak about the case where BOTH copy and move constructors are noexcept.

What if copy ctr throwable, and move constructor noexcept (for example std::vector<std::string>) ? What, in this case std::vector REQUIRED to use move? Or its juts too obvious for compiler that noexcept is faster path?

Also, please do point me where standard says something about vector reallocation behavior at all. I'm looking at 26.3.11 section of n4700 (C++17 draft), and can't find a word how exactly reallocation should happened. The closest I found is 26.3.11.3 "void reserve"; and 26.3.11.5 "void push_back"; neither specifying meaning of "reallocation". Or this hided somewhere in guts of Allocator?

Hyman Rosen

unread,
Oct 31, 2017, 11:26:42 AM10/31/17
to std-pr...@isocpp.org
On Tue, Oct 31, 2017 at 11:16 AM, <towe...@gmail.com> wrote:
Also, please do point me where standard says something about vector reallocation behavior at all.

The standard library should be specified by reference implementation. 

towe...@gmail.com

unread,
Oct 31, 2017, 11:56:08 AM10/31/17
to ISO C++ Standard - Future Proposals
Thank you. Is it in public domain? Can't google it, and honestly, first time hear of it...
Also, where it said so?

вторник, 31 октября 2017 г., 17:26:42 UTC+2 пользователь Hyman Rosen написал:

Hyman Rosen

unread,
Oct 31, 2017, 12:13:50 PM10/31/17
to std-pr...@isocpp.org
On Tue, Oct 31, 2017 at 11:56 AM, <towe...@gmail.com> wrote:
Thank you. Is it in public domain? Can't google it, and honestly, first time hear of it...
Also, where it said so?

вторник, 31 октября 2017 г., 17:26:42 UTC+2 пользователь Hyman Rosen написал:
The standard library should be specified by reference implementation. 

I said should be, not is.  But gcc, for example, provides implementations as free software
under GPL 3 with the GCC Runtime Library Exception.  Here is vector:
<https://gcc.gnu.org/onlinedocs/gcc-4.6.3/libstdc++/api/a01069_source.html>

If you have questions about move vs. copy on reallocation, it's much easier to see what
the implementation does, and maybe it will even have comments explaining the choices
made.  And you can try changing it if you have a better way, and get it updated.  It's
better than trying to parse the meaning of the standard about what the implementation
should do.  I don't have firsthand knowledge, but it wouldn't surprise me if the people
who wrote the standard library specifications were themselves looking at a reference
implementation while doing it.  The whole business with buckets in the unordered
containers specifications certainly seems like it.

Thiago Macieira

unread,
Oct 31, 2017, 12:31:12 PM10/31/17
to std-pr...@isocpp.org
On terça-feira, 31 de outubro de 2017 08:16:53 PDT towe...@gmail.com wrote:
> What if copy ctr throwable, and move constructor noexcept (for example
> std::vector<std::string>) ? What, in this case std::vector REQUIRED to use
> move? Or its juts too obvious for compiler that noexcept is faster path?

I was hoping to find it for vector::erase(), but it says

"Throws: Nothing unless an exception is thrown by the assignment operator or
move assignment operator of T."

So that one doesn't restrict to calling the move assignment operator. My next
bet would be shrink_to_fit(), which means we reallocate but do not add or
remove any items: I'd expect that function to be noexcept if the item type has
a noexcept move constructor. However, the text has a loophole:

" If an exception is thrown other than by the move constructor of a non-
CopyInsertable T there are no effects."

It restricts to the noexceptness to non-copyable types only.

Bo Persson

unread,
Nov 1, 2017, 7:05:33 AM11/1/17
to std-pr...@isocpp.org
On 2017-10-31 17:31, Thiago Macieira wrote:
> On terça-feira, 31 de outubro de 2017 08:16:53 PDT towe...@gmail.com wrote:
>> What if copy ctr throwable, and move constructor noexcept (for example
>> std::vector<std::string>) ? What, in this case std::vector REQUIRED to use
>> move? Or its juts too obvious for compiler that noexcept is faster path?
>
> I was hoping to find it for vector::erase(), but it says
>
> "Throws: Nothing unless an exception is thrown by the assignment operator or
> move assignment operator of T."
>
> So that one doesn't restrict to calling the move assignment operator. My next
> bet would be shrink_to_fit(), which means we reallocate but do not add or
> remove any items: I'd expect that function to be noexcept if the item type has
> a noexcept move constructor. However, the text has a loophole:
>
> " If an exception is thrown other than by the move constructor of a non-
> CopyInsertable T there are no effects."
>
> It restricts to the noexceptness to non-copyable types only.
>

Not exactly. It's rather assumed that if the move constructor can throw,
the implementation should prefer to copy instead. A throwing copy is
recoverable ("no effects"), as the original can be preserved until
everything has been copied.

A throwing move halfway through a reallocation will get us in deep
trouble. What are the odds that trying to move back some objects will
not throw again? So the implementation is allowed to just abandon
(destroy) the elements already moved, and so they are lost.

We can just hope that throwing move constructors are rare. Or that we
don't need to store such objects in a vector. Or, alternatively, that
losing some objects is not a big deal. YMMV.


Bo Persson


Bo Persson

unread,
Nov 1, 2017, 7:15:07 AM11/1/17
to std-pr...@isocpp.org
Or, reading it again, perhaps that's what you meant. :-)


Bo Persson


towe...@gmail.com

unread,
Nov 1, 2017, 11:56:03 AM11/1/17
to ISO C++ Standard - Future Proposals, b...@gmb.dk
You have prominent ability to read between the lines ;)
But what I see is:
"Standard guarantee, that vector/deque is allowed to by 100% move unaware. It allowed to use copy always." :)

And all these speculations based on just two lines, saying about side effects.
Where it said that it should do anything? Or even recommended?

Technical standard is not the Bible, but strict technical specification.

среда, 1 ноября 2017 г., 13:05:33 UTC+2 пользователь Bo Persson написал:

Thiago Macieira

unread,
Nov 1, 2017, 11:59:15 AM11/1/17
to std-pr...@isocpp.org
On quarta-feira, 1 de novembro de 2017 04:05:16 PDT Bo Persson wrote:
> We can just hope that throwing move constructors are rare. Or that we
> don't need to store such objects in a vector. Or, alternatively, that
> losing some objects is not a big deal. YMMV.

That was not the point. The question, to put it succintly, is:

if T is throwing-copyable and has a nothrow move constructor, is std::vector
required to use the move constructor?

Bo Persson

unread,
Nov 1, 2017, 12:18:30 PM11/1/17
to std-pr...@isocpp.org
On 2017-11-01 16:56, towe...@gmail.com wrote:
> You have prominent ability to read between the lines ;)
> But what I see is:
> "Standard guarantee, that vector/deque is allowed to by 100% move
> unaware. It allowed to use copy always." :)
>
> And all these speculations based on just two lines, saying about side
> effects.
> Where it said that it *should* do anything? Or even /recommended/?

This is not so much reading between the lines as remembering the reason
for adding the move_if_noexcept() helper function to the standard.

http://en.cppreference.com/w/cpp/utility/move_if_noexcept

Perhaps the standard is missing a "Use it!" directive for that function?


Bo Persson


>
> Technical standard is not the Bible, but strict technical specification.
>
> среда, 1 ноября 2017 г., 13:05:33 UTC+2 пользователь Bo Persson написал:
>
> On 2017-10-31 17:31, Thiago Macieira wrote:
> > On terça-feira, 31 de outubro de 2017 08:16:53 PDT

towe...@gmail.com

unread,
Nov 1, 2017, 12:46:06 PM11/1/17
to ISO C++ Standard - Future Proposals, b...@gmb.dk
>> ... as remembering the reason
>> for adding the move_if_noexcept() helper function to the standard.

Does standard contain those reasoning, or reference to it? Does standard should be deducted from reasons?
Does standard should be guided by reasons, or requirements?



>> Perhaps the standard is missing a "Use it!" directive for that function?
Sure, std::vector does not required to use move_if_noexcept. Though, I'm not sure this will solve the problem... Maybe will, by the way...

towe...@gmail.com

unread,
Nov 6, 2017, 12:20:41 AM11/6/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
So... If I convinced some of you that standard need stronger guarantees about move in vector... Or at least some defined behavior...
[I personally think that allow std::vector<std::vector<T>> to use copy on reallocation is not sane]
Well, I don't know the procedure... But as I can guess, only committee member may submit proposal / defect report.
If so - does someone [from people allowed to write such things :)] willing to actually write this?

If need - I think I can make a draft of this, though I'm not sure how exactly it should be...

Greg Marr

unread,
Nov 18, 2017, 2:08:47 PM11/18/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
On Monday, November 6, 2017 at 12:20:41 AM UTC-5, towe...@gmail.com wrote:
So... If I convinced some of you that standard need stronger guarantees about move in vector... Or at least some defined behavior...
[I personally think that allow std::vector<std::vector<T>> to use copy on reallocation is not sane]
Well, I don't know the procedure... But as I can guess, only committee member may submit proposal / defect report.

towe...@gmail.com

unread,
Nov 19, 2017, 5:02:25 AM11/19/17
to ISO C++ Standard - Future Proposals, towe...@gmail.com
Thank you. Is this should be classified as issue, or proposal?

суббота, 18 ноября 2017 г., 21:08:47 UTC+2 пользователь Greg Marr написал:
Reply all
Reply to author
Forward
0 new messages