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

reference to reference (Type &&)

70 views
Skip to first unread message

MarioCPPP

unread,
Mar 5, 2023, 11:15:12 AM3/5/23
to
Could sb explain me what is the semantic meaning of that
syntax and what possible use-cases does a Type && have ?

A reference is an "hidden" immutable pointer to an object
(with some syntactic sugar to access members by DOT and not
by ->), which should also a be warranty to point to a valid
object (no nullptr).

But a Type && seems to be also able to point to ... to what ?
What is for ?

--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
MarioCPPP

Öö Tiib

unread,
Mar 6, 2023, 1:24:09 AM3/6/23
to
On Sunday, 5 March 2023 at 18:15:12 UTC+2, MarioCPPP wrote:
> Could sb explain me what is the semantic meaning of that
> syntax and what possible use-cases does a Type && have ?
>
> A reference is an "hidden" immutable pointer to an object
> (with some syntactic sugar to access members by DOT and not
> by ->), which should also a be warranty to point to a valid
> object (no nullptr).
>
> But a Type && seems to be also able to point to ... to what ?
> What is for ?
>
It is rvalue reference or when function template argument then
forwarding reference. Read
<https://en.cppreference.com/w/cpp/language/reference>

Paavo Helde

unread,
Mar 6, 2023, 5:53:59 AM3/6/23
to
05.03.2023 18:14 MarioCPPP kirjutas:
> Could sb explain me what is the semantic meaning of that syntax  and
> what possible use-cases does a Type && have ?
>
> A reference is an "hidden" immutable pointer to an object (with some
> syntactic sugar to access members by DOT and not by ->), which should
> also a be warranty to point to a valid object (no nullptr).
>
> But a Type && seems to be also able to point to ... to what ?
> What is for ?

It's used to avoid deep copying of objects, either for better
performance or when a copy does not make sense, like a file stream. One
could think of it as a cleaner replacement of swap.





James Kuyper

unread,
Mar 6, 2023, 2:31:19 PM3/6/23
to
On 3/5/23 11:14, MarioCPPP wrote:
> Could sb explain me what is the semantic meaning of that syntax and
> what possible use-cases does a Type && have ?
>
> A reference is an "hidden" immutable pointer to an object (with some
> syntactic sugar to access members by DOT and not by ->), which should
> also a be warranty to point to a valid object (no nullptr).
>
> But a Type && seems to be also able to point to ... to what ?
> What is for ?

A key point to keep in mind is that when a '&' character is parsed as a
token, it can mean the unary address-of operator, the binary bitwise-and
operator, or it could indicate a reference in a type specification. In
none of those contexts would it ever make sense to use two consecutive
'&' tokens, and it is therefore not allowed by the grammar. Instead, a
pair of consecutive '&' characters is parsed as a single "&&" token.
Depending upon the context, that token could mean either the binary
logical-and operator, or as part of type declaration, it identifies an
rvalue reference.


Andrey Tarasevich

unread,
Mar 6, 2023, 5:00:31 PM3/6/23
to
On 03/05/23 8:14 AM, MarioCPPP wrote:
> But a Type && seems to be also able to point to ... to what ?
> What is for ?

`&&` should be perceived as a monolithic token, not a "reference to
reference". It is just a special kind of reference known as "rvalue
reference".

A formal answer for what it is would be: It is special kind of reference
that is shoehorned into the C++ type system with its own initialization
and overload resolution rules. Find the specification, read these rules
and use this kind of reference whenever it works for you.

A more human-friendly answer might sound as follows: The whole thing was
designed with one specific purpose in mind: move semantics. You can use
rvalue references and their properties to elegantly implement move
semantics. Note that move semantics is not a core-language concept, it
is something you implement yourself, manually, by taking advantage of
rvalue references' properties. Find a book on modern C++, read about
move semantics.

Finally, rvalue references can be useful for other purposes, not just
for their primary purpose (i.e. move semantics), so you can feel free to
use them wherever you consider appropriate.

--
Best regards,
Andrey


MarioCPPP

unread,
Mar 6, 2023, 5:22:52 PM3/6/23
to
now ... with the suggestions from You and others in the
thread, I am beginning to understand the very basic of what
it is (I did not know an R-value could even have a reference
... my fault !)

But I still cant'imagine its use.
I have to understand what is a MOVE SEMANTIC.

In my old-styled mind move IS copying (and deleting the
original object).

Others said "to avoid DEEP copying", which is a concept I
know, but still I am unable to connect with the R-value
reference concept.

Anyway, I will try to study further to understand if this
semantic will ever be useful to me.
Tnx to all

James Kuyper

unread,
Mar 6, 2023, 7:48:18 PM3/6/23
to
On 3/6/23 17:22, MarioCPPP wrote:
...
> But I still cant'imagine its use.
> I have to understand what is a MOVE SEMANTIC.
>
> In my old-styled mind move IS copying (and deleting the
> original object).

Only when necessary. What the semantics of moving are is up to the
implementor of the relevant special functions.

A typical example is when an object doesn't contain the actual data, but
only a pointer to the data. A copy constructor would copy the data that
was pointed at, and then put a pointer to the copied data in the new
object. The destructor would deallocate the memory the data was stored in.
A move constructor, on the other hand, would move the pointer from the
existing object to the new one, replacing the pointer in the existing
object with a null pointer to indicate that it no longer had any data.
The data that was pointed at would remain untouched. As a result, a move
can be much faster than a copy followed by a delete. The only point in
creating a move constructor is if it can, indeed, be faster than a copy
followed by a delete.

I don't know anywhere near as much about r-value references as other
people, so I'll let them try to explain how that is connected.

MarioCPPP

unread,
Mar 7, 2023, 6:19:57 AM3/7/23
to
seems really intresting (even if I am still unable to guess
a full example).
Well your explanation was actually closer to my starting
point and so more useful to me. :D

Malcolm McLean

unread,
Mar 7, 2023, 6:48:21 AM3/7/23
to
On Monday, 6 March 2023 at 22:22:52 UTC, MarioCPPP wrote:
> On 06/03/23 23:00, Andrey Tarasevich wrote:
>
> now ... with the suggestions from You and others in the
> thread, I am beginning to understand the very basic of what
> it is (I did not know an R-value could even have a reference
> ... my fault !)
>
> But I still cant'imagine its use.
> I have to understand what is a MOVE SEMANTIC.
>
Essentially it is this.

struct foo
{
unsigned char *data;
size_t N;
};

void deep_copy(struct foo *dest, const struct foo *source)
{
free(dest->data);
dest->data = malloc(source->N);
memcpy(dest->data, source->data, source->N):
dest->N = source->N;
}

If N is large, this is quite expensive.

void move(struct foo *dest, struct foo * source)
{
unsigned char *temp = dest->data;
dest->data = source->data;
source->data = temp;
size_t tempN = dest->N
dest->N = source->N;
source->N = tempN;
}

This is only a few machine instructions. But source is no longer a constant, it
is altered by the operation. It's left in a valid but unusable state. In fact it is
set to the old value of dest, but this isn't part of the specification and isn't guaranteed.

All C++ move semantics is is wrapping this idea in nice syntax.

Öö Tiib

unread,
Mar 7, 2023, 7:37:18 AM3/7/23
to
On Tuesday, 7 March 2023 at 13:48:21 UTC+2, Malcolm McLean wrote:
>
> void move(struct foo *dest, struct foo * source)

You wrote a swap named as "move". That can confuse.
Swap may fit with most loose requirements of move but
usually is not used as that.

Other purpose of move are objects that represent something
that can not (or should not) be copied. Like file descriptors,
sockets or threads. There move is used as way of passing
of ownership.

Mut...@dastardlyhq.com

unread,
Mar 7, 2023, 10:58:35 AM3/7/23
to
On Tue, 7 Mar 2023 03:48:11 -0800 (PST)
Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>All C++ move semantics is is wrapping this idea in nice syntax.

Nice syntax but leaving the danger as you say of the source in a potentially
unusable state. IMO move() is best avoided unless you really know what you're
doing but unfortunately a lot of people think its just a quick copy without
realising the consequences.

Paavo Helde

unread,
Mar 7, 2023, 5:10:56 PM3/7/23
to
07.03.2023 17:58 Mut...@dastardlyhq.com kirjutas:
> On Tue, 7 Mar 2023 03:48:11 -0800 (PST)
> Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>> All C++ move semantics is is wrapping this idea in nice syntax.
>
> Nice syntax but leaving the danger as you say of the source in a potentially
> unusable state.

This exact danger is why we have rvalue references in the language, so
that dangerous bindings are mostly avoided. Either there is a genuine
rvalue whose final state would not be important anyway, or we use
std::move() to explicitly declare we do not care about the final state
of the object.

> IMO move() is best avoided unless you really know what you're
> doing but unfortunately a lot of people think its just a quick copy without
> realising the consequences.

Here you are right, rvalue references should be used only by people who
know what they are doing, similar to any other feature in C++.


Mut...@dastardlyhq.com

unread,
Mar 8, 2023, 4:21:20 AM3/8/23
to
On Wed, 8 Mar 2023 00:10:43 +0200
Paavo Helde <ees...@osa.pri.ee> wrote:
>07.03.2023 17:58 Mut...@dastardlyhq.com kirjutas:
>> On Tue, 7 Mar 2023 03:48:11 -0800 (PST)
>> Malcolm McLean <malcolm.ar...@gmail.com> wrote:
>>> All C++ move semantics is is wrapping this idea in nice syntax.
>>
>> Nice syntax but leaving the danger as you say of the source in a potentially
>> unusable state.
>
>This exact danger is why we have rvalue references in the language, so
>that dangerous bindings are mostly avoided. Either there is a genuine
>rvalue whose final state would not be important anyway, or we use
>std::move() to explicitly declare we do not care about the final state
>of the object.

Thats fine, until its used in a library and someone doesn't look at the
code so don't realise the object they passed in is now invalid. Yes thats
a fairly unlikely scenario but something that needs to be considered when
using move. Will someone use your code who isn't aware of what you've done?

Paavo Helde

unread,
Mar 8, 2023, 7:41:46 AM3/8/23
to
Care to bring an example? I have hard time figuring out how one could
get a moved-away object via rvalue references inadvertently.


Mut...@dastardlyhq.com

unread,
Mar 8, 2023, 11:26:26 AM3/8/23
to
On Wed, 8 Mar 2023 14:41:30 +0200
#include <iostream>
#include <string>

using namespace std;

void myfunc(string &s1)
{
string s2 = move(s1);
}


int main()
{
string s1 = "hello";
myfunc(s1);
cout << s1 << endl;
return 0;
}

Alf P. Steinbach

unread,
Mar 8, 2023, 12:08:56 PM3/8/23
to
Entirely safe code.

A move from any standard library object leaves the object in a
well-defined state.

The originally proposed rules didn't completely guarantee all desired
safety, but as I recall Scott Meyers posted in this very group about it,
and after some discussion reasonable compromise was reached. :)


- Alf

PS: You don't need the `return 0;`; that's the default for `main` in
both C and C++.


Paavo Helde

unread,
Mar 8, 2023, 12:23:30 PM3/8/23
to
There is no rvalue reference in this example, only a non-const lvalue
reference which is known to be error-prone and tricky to use. Use rvalue
references instead:

#include <iostream>
#include <string>

using namespace std;

void myfunc(string &&s1)
{
string s2 = move(s1);
}


int main()
{
string s1 = "hello";
// myfunc(s1); // does not compile
myfunc(std::move(s1)); // ok, explicit move

MarioCPPP

unread,
Mar 8, 2023, 8:19:26 PM3/8/23
to
^^^
does not seem that "string && s1" that I was asking about,
though ....

> {
> string s2 = move(s1);
> }
>
>
> int main()
> {
> string s1 = "hello";
> myfunc(s1);
> cout << s1 << endl;
> return 0;
> }
>

Bonita Montero

unread,
Mar 10, 2023, 1:57:06 AM3/10/23
to
Am 08.03.2023 um 18:23 schrieb Paavo Helde:

> void myfunc(string &&s1)
> {
>         string s2 = move(s1);
> }

Do it the _easy_ way:

template<typename String>
requires same_as<String, basic_string<String::char_type,
String::traits_type, String::allocator>>
void myfunc( String &&str )
{
String str2( forward<String>( str ) ) );
}

Works with any basic_string variant and with
any of the four forwarding reference types.
0 new messages