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

Is std::move() a misleading misnomer?

90 views
Skip to first unread message

Juha Nieminen

unread,
Jul 15, 2022, 8:02:28 AM7/15/22
to
Let's take a relatively simple line of code:

v1 = std::move(v2);

What does that do? Well, we know for certain (at least if the type of 'v1'
and 'v2' has been logically implemented) that 'v1' will contain what
'v2' contained before this statement.

But what will 'v2' contain?

It depends. (More particularly, it depends on whether the type in
question implements a move assignment operator, and it has been
implemented in the recommended way.)

But should it really depend on anything? Isn't that statement
expressing the desire to *move* the contents of 'v2' into 'v1',
thus leaving 'v2' empty? (Even if under the hood a deep copy
is being done instead, consistency of behavior would dictate
for 'v2' to become empty nevertheless.)

The fact is that if we don't know the type of 'v1' and 'v2'
we cannot assume that 'v2' will be empty.

Since std::move() does not guarantee that the contents will be
moved, isn't it quite a misnomer?

std::to_rvalue() might be more cryptic, but at least it would
express much better what it's actually doing.

Bonita Montero

unread,
Jul 15, 2022, 8:43:07 AM7/15/22
to
That actually only does happen when v1 is a templated data type.
Otherwise you'll know whether v1 has a move assignment operator.
If it dosn't that's actually only a performance problem, but not
a semantical problem since you can expect the copy assignment
operator to make a copy.

Malcolm McLean

unread,
Jul 15, 2022, 9:18:10 AM7/15/22
to
It doesn't actually move it.
But v2 become invalid. Move assignments of big objects are often
implemented as swaps. You exchange a few pointers and v1 becomes
equal to v2. But you don't guarantee this because it is a side-effect
rather than the operation you want.

Bo Persson

unread,
Jul 15, 2022, 10:02:05 AM7/15/22
to
On 2022-07-15 at 14:02, Juha Nieminen wrote:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
>
> What does that do? Well, we know for certain (at least if the type of 'v1'
> and 'v2' has been logically implemented) that 'v1' will contain what
> 'v2' contained before this statement.
>
> But what will 'v2' contain?


Why do we care? You move from v2 when you don't need to use its value
anymore.

>
> It depends. (More particularly, it depends on whether the type in
> question implements a move assignment operator, and it has been
> implemented in the recommended way.)
>
> But should it really depend on anything? Isn't that statement
> expressing the desire to *move* the contents of 'v2' into 'v1',
> thus leaving 'v2' empty? (Even if under the hood a deep copy
> is being done instead, consistency of behavior would dictate
> for 'v2' to become empty nevertheless.)

If you pass v2 as a parameter to some other function (or constructor)
just before v2 goes out of scope, why spend time making it "empty" just
before it goes out of scope?


>
> The fact is that if we don't know the type of 'v1' and 'v2'
> we cannot assume that 'v2' will be empty.

No we cannot. So what?

>
> Since std::move() does not guarantee that the contents will be
> moved, isn't it quite a misnomer?
>
> std::to_rvalue() might be more cryptic, but at least it would
> express much better what it's actually doing.

There has been proposals for more "obvious" names like enable_move or
allow_move, or make_movable. But they all suffer from the problem of
being a lot longer to type. And not much easier to read.

So "move" has the advantage of being short. We just have to learn what
it means. Can't be much harder than to figure out what constexpr or
decltype means.

Gawr Gura

unread,
Jul 15, 2022, 11:34:38 AM7/15/22
to
> It depends. (More particularly, it depends on whether the type in
> question implements a move assignment operator, and it has been
> implemented in the recommended way.)

Isn't this functionally true of most lines of a C++ program? Between
template specialization, overloading, and user defined implicit casts
there are a lot of ways to make something that seems straight forward
behave in a roundabout way. Why should it matter that the move
assignment operator might behave in such a way?

Jorgen Grahn

unread,
Jul 15, 2022, 12:03:15 PM7/15/22
to
On Fri, 2022-07-15, Juha Nieminen wrote:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
...
> Since std::move() does not guarantee that the contents will be
> moved, isn't it quite a misnomer?
>
> std::to_rvalue() might be more cryptic, but at least it would
> express much better what it's actually doing.

I'm pretty sure Stroustrup writes, when he documents std::move in
TC++PL, that he would have preferred a name similar to to_rvalue().

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Bonita Montero

unread,
Jul 15, 2022, 1:08:54 PM7/15/22
to
Am 15.07.2022 um 15:18 schrieb Malcolm McLean:

> It doesn't actually move it.
> But v2 become invalid. Move assignments of big objects are often
> implemented as swaps. ...

std::swap uses move-semantics if implemented by the object to be
swapped (move constructor and move assignment operator). But moves
themselfes are usually not implemented as swaps.

Bonita Montero

unread,
Jul 15, 2022, 1:11:39 PM7/15/22
to
Am 15.07.2022 um 18:03 schrieb Jorgen Grahn:

> I'm pretty sure Stroustrup writes, when he documents std::move in
> TC++PL, that he would have preferred a name similar to to_rvalue().

I never read that in that book and I currently can't find that neither
in the German nor in the English version. I think that move is simply
semantically sufficient and there are no needs to further emphasize
this in another name.

Alf P. Steinbach

unread,
Jul 15, 2022, 5:09:10 PM7/15/22
to
On 15 Jul 2022 14:02, Juha Nieminen wrote:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
>
> What does that do? Well, we know for certain (at least if the type of 'v1'
> and 'v2' has been logically implemented) that 'v1' will contain what
> 'v2' contained before this statement.
>
> But what will 'v2' contain?
>
> It depends. (More particularly, it depends on whether the type in
> question implements a move assignment operator, and it has been
> implemented in the recommended way.)
>
> But should it really depend on anything? Isn't that statement
> expressing the desire to *move* the contents of 'v2' into 'v1',
> thus leaving 'v2' empty? (Even if under the hood a deep copy
> is being done instead, consistency of behavior would dictate
> for 'v2' to become empty nevertheless.)
>
> The fact is that if we don't know the type of 'v1' and 'v2'
> we cannot assume that 'v2' will be empty.
>
> Since std::move() does not guarantee that the contents will be
> moved, isn't it quite a misnomer?

Yes, it misleads people. In particular students.


> std::to_rvalue() might be more cryptic, but at least it would
> express much better what it's actually doing.

Well, consider `std::to_movable`.

It would IMO be a good alternate or replacement name for `std::move`,
because unlike the verb it just expresses a potential, a potential that
may or may not manifest depending on the types.

Unfortunately the shorter name `std::movable` is now taken by a C++20
concept.

- Alf

Paavo Helde

unread,
Jul 16, 2022, 4:49:04 AM7/16/22
to
15.07.2022 15:02 Juha Nieminen kirjutas:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
>
> What does that do? Well, we know for certain (at least if the type of 'v1'
> and 'v2' has been logically implemented) that 'v1' will contain what
> 'v2' contained before this statement.
>
> But what will 'v2' contain?
>
> It depends. (More particularly, it depends on whether the type in
> question implements a move assignment operator, and it has been
> implemented in the recommended way.)

Yes, it is a bit misleading name, but not so much as e.g. "static" or
"inline" ;-)

That's also why when I'm implementing move ctor/assignment for my
classes, I always take care to make v2 properly "empty" even when not
needed technically, just for consistency.

In general one shouldn't care about what state v2 is left in, so the
issue is moot.

In case I care, e.g. when preparing a data structure containing
single-threaded smart pointers to be sent over to another thread, I add
an extra assert line to be sure the move actually emptied v2.

Juha Nieminen

unread,
Jul 16, 2022, 2:21:38 PM7/16/22
to
Bo Persson <b...@bo-persson.se> wrote:
> There has been proposals for more "obvious" names like enable_move or
> allow_move, or make_movable. But they all suffer from the problem of
> being a lot longer to type.

And that's a problem because...

Bo Persson

unread,
Jul 16, 2022, 3:12:00 PM7/16/22
to
Because std::move(x) is a shorthand for static_cast<T&&>(x) and one
important criteria for a shorthand is that is should be shorter.



Jorgen Grahn

unread,
Jul 16, 2022, 3:24:36 PM7/16/22
to
On Fri, 2022-07-15, Bonita Montero wrote:
> Am 15.07.2022 um 18:03 schrieb Jorgen Grahn:
>
>> I'm pretty sure Stroustrup writes, when he documents std::move in
>> TC++PL, that he would have preferred a name similar to to_rvalue().
>
> I never read that in that book and I currently can't find that neither
> in the German nor in the English version.

Fourth edition, page 516, 17.5.2. "It would have been better if move()
had been called rval(), but ...".

Chris M. Thomasson

unread,
Jul 16, 2022, 7:31:23 PM7/16/22
to
On 7/15/2022 5:02 AM, Juha Nieminen wrote:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
>
> What does that do? Well, we know for certain (at least if the type of 'v1'
> and 'v2' has been logically implemented) that 'v1' will contain what
> 'v2' contained before this statement.
>
> But what will 'v2' contain?

For some reason, I thought of a move from v2 to v1 leaves v2 in an
undefined zombie state where accessing v2 is undefined behavior because
its now "dead"?

Malcolm McLean

unread,
Jul 16, 2022, 9:32:44 PM7/16/22
to
On Sunday, 17 July 2022 at 00:31:23 UTC+1, Chris M. Thomasson wrote:
> On 7/15/2022 5:02 AM, Juha Nieminen wrote:
> > Let's take a relatively simple line of code:
> >
> > v1 = std::move(v2);
> >
> > What does that do? Well, we know for certain (at least if the type of 'v1'
> > and 'v2' has been logically implemented) that 'v1' will contain what
> > 'v2' contained before this statement.
> >
> > But what will 'v2' contain?
> For some reason, I thought of a move from v2 to v1 leaves v2 in an
> undefined zombie state where accessing v2 is undefined behavior because
> its now "dead"?
>
That's more or less right. v2 has to be put into a valid state. So the destructor
must work. Also, it can be assigned to, and I believe that the assignment operator
must work.
But it can be and commonly is in a null state. However that shouldn't be treated as
guaranteed. An employee class might contain no employees, it might contain
one employee, or it might contain the full list of employees (the move assignment
was in fact implemented as a copy) or it might contain v1's list of employees
(the move assignment was implemented as a pointer swap).
It not really a case of UB. It's about the implied contract the move assignment
operator makes.

Juha Nieminen

unread,
Jul 17, 2022, 10:29:04 AM7/17/22
to
I have never subscribed to the "brevity over clarity" principle of
programming (which seems awfully popular).

You cannot compare two extremes and argue that the shorter extreme
is better because it's shorter, without taking into account the
middle possibilities that are clearer than both extremes.

wij

unread,
Jul 18, 2022, 7:14:48 PM7/18/22
to
From my perspective, the committee seemed urgent to standardize what the
'move ctor' means. Like always(?), the committee likes to show it always has a
better resolution (unproven, e.g. std::endl) then requested.

General rule: Complexity don't go away.
C++ statement can be shorter (or smarter, whatever), but the 'hidden' comments
become longer, the 'comment' can be shorter if put into 'standard'. To understand
'standard', one has to expand 'comment' to, say, ordinary/original form...
... Complexity is just moved around, won't disappear. (another case is in the error handling).
The rule applies to almost anything. IMO, the goal should be 'efficiency'.

Snippet of the manpage of the move ctor for template class Array (written before 2006):
No issues of rref. The only problem encountered is with classes like BigInt
BitInt::operator=(T) when T is arithmetic types, lots of overloads are needed.
But such needs are never demanding enough (IMO) for a core language change
to add rref.

Array(Array& src, ByMove_t)

Move semantics of src to this pointed address.

After function completed, src is regarded non-existant.
The space occupied by src can be recycled. The dummy argument type
ByMove_t acts as a function signature, use instance should always
be ByMove. This member is supposed to be invoked only via
placement new or in member initialization list, not to be
explicitly called otherwise.

This member is not entirely a real constructor (no new object is
ever created, so it is called move) but a function in constructor
form to support object movement in a dynamic array.

Note: Except in member initialization list, src must be
a whole type, not reference to a base. Otherwise,
effect is undefined.

Note: In cases element type T is a class that can use ::memcpy(2)
to do the job, user needs to provide a template
Wy::TypeTrait<T> overload, e.g.

template<> struct Wy::TypeTrait<T> {
static constexpr bool memcpy_able=true;
static constexpr bool memset_able=false;
};

[Effect]
size()= src.size()
capacity()= src.capacity()
begin()= src.begin()
end()= src.end()

At final, object src does not exist

Example:
class D : public B { // assume B has the move constructor
struct timespec m_ts;
char* m_ptr; // pointed to allocated memory
public:
D(D& d, ByMove_t)
: B(d,ByMove), m_ts(d.m_ts), m_ptr(d.m_ptr) {};
};

Bonita Montero

unread,
Jul 19, 2022, 1:58:58 AM7/19/22
to
Reads like someone who actually feels also unsettled by something like
that, but who wants to show others that he can understand the decisions
of the committee in order to make himself believe that he stands above
this.

Chris M. Thomasson

unread,
Jul 19, 2022, 7:41:56 PM7/19/22
to
Okay. That makes sense. Thank you.

Anthony Capobianco

unread,
Jul 20, 2022, 8:11:52 AM7/20/22
to
On Friday, July 15, 2022 at 2:02:28 PM UTC+2, Juha Nieminen wrote:
> Since std::move() does not guarantee that the contents will be
> moved, isn't it quite a misnomer?
>
> std::to_rvalue() might be more cryptic, but at least it would
> express much better what it's actually doing.

The name of the function doesn't describe what it does, it describes the intent of a dev using it.
You use it when you intend to move something. If you only intend to make it an rvalue reference, then you can cast it.
The intent is different. The compiler doesn't care what you call it, your coworkers might.

Paavo Helde

unread,
Jul 20, 2022, 8:26:26 AM7/20/22
to
+1

Andrey Tarasevich

unread,
Jul 20, 2022, 4:08:51 PM7/20/22
to
On 7/15/2022 5:02 AM, Juha Nieminen wrote:
> Let's take a relatively simple line of code:
>
> v1 = std::move(v2);
>
> What does that do? Well, we know for certain (at least if the type of 'v1'
> and 'v2' has been logically implemented) that 'v1' will contain what
> 'v2' contained before this statement.
>
> But what will 'v2' contain?
>
> It depends. (More particularly, it depends on whether the type in
> question implements a move assignment operator, and it has been
> implemented in the recommended way.)
>
> But should it really depend on anything? Isn't that statement
> expressing the desire to *move* the contents of 'v2' into 'v1',
> thus leaving 'v2' empty?

No.

From the very beginning the concept of "moving" in C++ was designed
with the following axiom in mind:

Good-old classic copying is also a form of moving. Moving is a wider
concept, an extension of copying. Copying in that sense is a "laziest"
form of moving. ("Laziest" is a sense that it requires the last amount
of learning: i.e. one can just keep copying as before, and call it "moving")

It is very important to understand that in C++ full-blown, completely
non-destructive copying also qualifies as moving.

--
Best regards,
Andrey


Manfred

unread,
Jul 20, 2022, 7:16:27 PM7/20/22
to
On 7/20/2022 2:11 PM, Anthony Capobianco wrote:
> On Friday, July 15, 2022 at 2:02:28 PM UTC+2, Juha Nieminen wrote:
>> Since std::move() does not guarantee that the contents will be
>> moved, isn't it quite a misnomer?
>>
>> std::to_rvalue() might be more cryptic, but at least it would
>> express much better what it's actually doing.
>
> The name of the function doesn't describe what it does, it describes the intent of a dev using it.

Correct in this case, however the name of most standard functions
describes what the functions do.

> You use it when you intend to move something. If you only intend to make it an rvalue reference, then you can cast it.
> The intent is different. The compiler doesn't care what you call it, your coworkers might.

Good explanation.
Still, the name "move" is somewhat problematic, if even Bjarne's
description of the function has to explain from the very beginning that
std::move doesn't move anything.

So, yes the name is not perfect, but alternatives could be worse, and we
can live with not perfect.

Fred. Zwarts

unread,
Jul 21, 2022, 3:47:54 AM7/21/22
to
Op 20.jul..2022 om 14:11 schreef Anthony Capobianco:
That is in general difficult to implement, because it is not the dev
using the function who give it a name, but the dev creating the function
who defines the name. When creating a function it might not be clear in
how many ways it can be used. Sometimes functions are used in unexpected
ways, not foreseen when creating the function. Therefore, when creating
a function, one usually give it a name according to what it does. If it
has a name according to its intended use, it may become very confusing
when it is used for other purposes.
That said, in this case it seems that it is very improbable that
std::move will be used for other purposes than moving.

Juha Nieminen

unread,
Jul 21, 2022, 5:06:11 AM7/21/22
to
I think that the name 'std::move()' breaks more clarity conventions than
that.

'move()', as a verb, would indicate that the function itself does something,
in this case, that it does the moving. It would be completely concordant
with myriads of other such functions, like std::sort() (which itself does
the sorting), std::find() (which itself does the finding), std::copy()
(which itself does the copying) and so on.

Particularly that last one is extremely telling: Compare "std::copy()"
to "std::move()". They both sound, by their name, extraordinarily
closely related. It sounds like the former copies stuff from one
place to another (doing it itself), while the latter moves stuff
from one place to another (likewise doing it itself).

It would be highly bizarre if "std::copy()" would mean "this doesn't
actually copy anything, it just casts the parameter in such a manner
that it becomes copyable".

If the latter thing were needed, certainly it would be named something
clearer, like for example std::make_copyable(), or std::cast_to_copyable()
or something along those lines. Not just std::copy().

Likewise it would be highly bizarre if "std::sort()" did no sorting but
instead just somehow cast the parameter into something that can be sorted.

std::move() is extraordinarily unique among all standard library functions
in that it breaks this fundamental naming convention. Certainly someone
who knows well what std::copy() does but doesn't know what std::move()
does (but knows about move semantics) would assume that the latter does
the same thing as the former, but moving the elements instead of copying
them.

It can't even be a question of "move" being a short name. The standard
library doesn't shy away from using very long names if needed. Take for
example:

std::filesystem::recursive_directory_iterator::disable_recursion_pending()

Paavo Helde

unread,
Jul 21, 2022, 6:12:31 AM7/21/22
to
21.07.2022 12:05 Juha Nieminen kirjutas:

>
> Likewise it would be highly bizarre if "std::sort()" did no sorting but
> instead just somehow cast the parameter into something that can be sorted.

Yes, but e.g. std::unique() does not actually make a vector to contain
unique values. Like std::move(), it is only intended to be used as one
step toward the goal.

If moving was done by e.g. static_cast<T&&>(x), then each such code line
would need a comment a la "moving the value". Think of std::move() as a
replacement of this comment.



red floyd

unread,
Jul 21, 2022, 4:09:20 PM7/21/22
to
Probably better would have been std::moveable() instead of std:move.


0 new messages