Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Verbosity when optimizing with rvalue references

Reply
Thread Tools

Verbosity when optimizing with rvalue references

 
 
Sousuke
Guest
Posts: n/a
 
      06-24-2010
The optimizations that rvalue references make possible are nice, but
I'm having one problem with them (rvalue refs): they sometimes lead to
too much verbosity.

When defining a constructor or a setter, you usually take a const T&
and assign it to one of the class's members. In C++0x, you can
additionally define an overload that takes a T&&:

class OneString
{
public:
OneString(const string& s) : m_s(s)
{
}

OneString(string&& s) : m_s(move(s))
{
}

private:
string m_s;
};

One additional overload is not too much verbosity, but see the two-
argument case:

class TwoStrings
{
public:
TwoStrings(const string& s1, const string& s2) : m_s1(s1),
m_s2(s2)
{
}

TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
m_s2(s2)
{
}

TwoStrings(const string& s1, string&& s2) : m_s1(s1),
m_s2(move(s2))
{
}

TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
m_s2(move(s2))
{
}

private:
string m_s1;
string m_s2;
};

I don't even know how many overloads would there be for 3 arguments
(27 maybe?).

Is there a way to avoid this verbosity?
 
Reply With Quote
 
 
 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      06-24-2010
* Sousuke, on 24.06.2010 17:23:
> The optimizations that rvalue references make possible are nice, but
> I'm having one problem with them (rvalue refs): they sometimes lead to
> too much verbosity.
>
> When defining a constructor or a setter, you usually take a const T&
> and assign it to one of the class's members. In C++0x, you can
> additionally define an overload that takes a T&&:
>
> class OneString
> {
> public:
> OneString(const string& s) : m_s(s)
> {
> }
>
> OneString(string&& s) : m_s(move(s))
> {
> }
>
> private:
> string m_s;
> };
>
> One additional overload is not too much verbosity, but see the two-
> argument case:
>
> class TwoStrings
> {
> public:
> TwoStrings(const string& s1, const string& s2) : m_s1(s1),
> m_s2(s2)
> {
> }
>
> TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
> m_s2(s2)
> {
> }
>
> TwoStrings(const string& s1, string&& s2) : m_s1(s1),
> m_s2(move(s2))
> {
> }
>
> TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
> m_s2(move(s2))
> {
> }
>
> private:
> string m_s1;
> string m_s2;
> };
>
> I don't even know how many overloads would there be for 3 arguments
> (27 maybe?).
>
> Is there a way to avoid this verbosity?


With the disclaimer that I haven't actually used C++0x rvalue references, why do
you want the ordinary reference overloads? I thought much of the point was that
an rvalue reference could deal with both cases. Isn't it so?


Cheers & hth.,

- Alf


--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
 
 
 
Sousuke
Guest
Posts: n/a
 
      06-24-2010
On Jun 24, 10:28*am, "Leigh Johnston" <(E-Mail Removed)> wrote:
> "Sousuke" <(E-Mail Removed)> wrote in message
>
> news:(E-Mail Removed)...
>
>
>
>
>
> > The optimizations that rvalue references make possible are nice, but
> > I'm having one problem with them (rvalue refs): they sometimes lead to
> > too much verbosity.

>
> > When defining a constructor or a setter, you usually take a const T&
> > and assign it to one of the class's members. In C++0x, you can
> > additionally define an overload that takes a T&&:

>
> > class OneString
> > {
> > public:
> > * *OneString(const string& s) : m_s(s)
> > * *{
> > * *}

>
> > * *OneString(string&& s) : m_s(move(s))
> > * *{
> > * *}

>
> > private:
> > * *string m_s;
> > };

>
> > One additional overload is not too much verbosity, but see the two-
> > argument case:

>
> > class TwoStrings
> > {
> > public:
> > * *TwoStrings(const string& s1, const string& s2) : m_s1(s1),
> > m_s2(s2)
> > * *{
> > * *}

>
> > * *TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
> > m_s2(s2)
> > * *{
> > * *}

>
> > * *TwoStrings(const string& s1, string&& s2) : m_s1(s1),
> > m_s2(move(s2))
> > * *{
> > * *}

>
> > * *TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
> > m_s2(move(s2))
> > * *{
> > * *}

>
> > private:
> > * *string m_s1;
> > * *string m_s2;
> > };

>
> > I don't even know how many overloads would there be for 3 arguments
> > (27 maybe?).

>
> > Is there a way to avoid this verbosity?

>
> Make the constructor a function template and use perfect forwarding perhaps?
> Not ideal as it is no longer obvious as to what types the constructor takes.


Also, calling std::move on the parameters would make them into rvalues
even if the respective arguments were lvalues!
 
Reply With Quote
 
Bo Persson
Guest
Posts: n/a
 
      06-24-2010
Sousuke wrote:
> On Jun 24, 10:28 am, "Leigh Johnston" <(E-Mail Removed)> wrote:
>> "Sousuke" <(E-Mail Removed)> wrote in message
>>

[...]
>>
>>> TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
>>> m_s2(move(s2))
>>> {
>>> }

>>
>>> private:
>>> string m_s1;
>>> string m_s2;
>>> };

>>
>>> I don't even know how many overloads would there be for 3
>>> arguments (27 maybe?).

>>
>>> Is there a way to avoid this verbosity?

>>
>> Make the constructor a function template and use perfect
>> forwarding perhaps? Not ideal as it is no longer obvious as to
>> what types the constructor takes.

>
> Also, calling std::move on the parameters would make them into
> rvalues even if the respective arguments were lvalues!


That's why we also have std:.forward to handle the template case.


Bo Persson


 
Reply With Quote
 
Sousuke
Guest
Posts: n/a
 
      06-24-2010
On Jun 24, 11:11*am, "Alf P. Steinbach /Usenet" <alf.p.steinbach
(E-Mail Removed)> wrote:
> * Sousuke, on 24.06.2010 17:23:
>
>
>
>
>
> > The optimizations that rvalue references make possible are nice, but
> > I'm having one problem with them (rvalue refs): they sometimes lead to
> > too much verbosity.

>
> > When defining a constructor or a setter, you usually take a const T&
> > and assign it to one of the class's members. In C++0x, you can
> > additionally define an overload that takes a T&&:

>
> > class OneString
> > {
> > public:
> > * * *OneString(const string& *s) : m_s(s)
> > * * *{
> > * * *}

>
> > * * *OneString(string&& *s) : m_s(move(s))
> > * * *{
> > * * *}

>
> > private:
> > * * *string m_s;
> > };

>
> > One additional overload is not too much verbosity, but see the two-
> > argument case:

>
> > class TwoStrings
> > {
> > public:
> > * * *TwoStrings(const string& *s1, const string& *s2) : m_s1(s1),
> > m_s2(s2)
> > * * *{
> > * * *}

>
> > * * *TwoStrings(string&& *s1, const string& *s2) : m_s1(move(s1)),
> > m_s2(s2)
> > * * *{
> > * * *}

>
> > * * *TwoStrings(const string& *s1, string&& *s2) : m_s1(s1),
> > m_s2(move(s2))
> > * * *{
> > * * *}

>
> > * * *TwoStrings(string&& *s1, string&& *s2) : m_s1(move(s1)),
> > m_s2(move(s2))
> > * * *{
> > * * *}

>
> > private:
> > * * *string m_s1;
> > * * *string m_s2;
> > };

>
> > I don't even know how many overloads would there be for 3 arguments
> > (27 maybe?).

>
> > Is there a way to avoid this verbosity?

>
> With the disclaimer that I haven't actually used C++0x rvalue references, why do
> you want the ordinary reference overloads? I thought much of the point was that
> an rvalue reference could deal with both cases. Isn't it so?


Well, rvalue references still respect const correctness. Besides, the
point of rvalue references is objects can be constructed/assigned to
by "stealing" from objects that are about to be destructed (rvalues),
so you need to know whether the source object is actually an rvalue
(because stealing from an object that is not about to be destructed
(lvalue) would be wrong). So I think the answer is no. But I'm still
learning about this and it's confusing
 
Reply With Quote
 
Bo Persson
Guest
Posts: n/a
 
      06-24-2010
Alf P. Steinbach /Usenet wrote:
> * Sousuke, on 24.06.2010 17:23:
>> The optimizations that rvalue references make possible are nice,
>> but I'm having one problem with them (rvalue refs): they sometimes
>> lead to too much verbosity.
>>
>> When defining a constructor or a setter, you usually take a const
>> T& and assign it to one of the class's members. In C++0x, you can
>> additionally define an overload that takes a T&&:
>>
>> class OneString
>> {
>> public:
>> OneString(const string& s) : m_s(s)
>> {
>> }
>>
>> OneString(string&& s) : m_s(move(s))
>> {
>> }
>>
>> private:
>> string m_s;
>> };
>>
>> One additional overload is not too much verbosity, but see the two-
>> argument case:
>>
>> class TwoStrings
>> {
>> public:
>> TwoStrings(const string& s1, const string& s2) : m_s1(s1),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(const string& s1, string&& s2) : m_s1(s1),
>> m_s2(move(s2))
>> {
>> }
>>
>> TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
>> m_s2(move(s2))
>> {
>> }
>>
>> private:
>> string m_s1;
>> string m_s2;
>> };
>>
>> I don't even know how many overloads would there be for 3 arguments
>> (27 maybe?).
>>
>> Is there a way to avoid this verbosity?

>
> With the disclaimer that I haven't actually used C++0x rvalue
> references, why do you want the ordinary reference overloads? I
> thought much of the point was that an rvalue reference could deal
> with both cases. Isn't it so?
>


No, it didn't turn out that way in the end.

Just like temporaries just bind to const lvalue references, lvalues
will not bind to rvalue references. Only in the case where the
parameter type is a template, can you use the reference collapsing
rules to turn type& && into type&.


Bo Persson


 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      06-24-2010
On 24 Jun., 17:23, Sousuke wrote:
> The optimizations that rvalue references make possible are nice, but
> I'm having one problem with them (rvalue refs): they sometimes lead to
> too much verbosity.
>
> When defining a constructor or a setter, you usually take a const T&
> and assign it to one of the class's members. In C++0x, you can
> additionally define an overload that takes a T&&:
>
> class OneString
> {
> public:
> * * OneString(const string& s) : m_s(s)
> * * {}
> * * OneString(string&& s) : m_s(move(s))
> * * {}
> private:
> * * string m_s;
> };
>
> One additional overload is not too much verbosity, but see the two-
> argument case:


In this particular instance (constructor, the members don't yet exist)
I'd simply take the strings by value. This also scales well for
multiple string parameters:

class ThreeStrings
{
public:
* * ThreeStrings(string s1, string s2, string s3)
: m_s1(move(s1)), m_s2(move(s2)), m_s3(move(s3))
* * {}
private:
* * string m_s1;
string m_s2;
string m_s3;
};

This works perfectly because we know that std::string supports quick
moving and because m_s1, m_s2 and m_s3 don't yet exist:

argument was #copies #moves
---------------------------------------------
lvalue 1 1
rvalue 0 2 (or 1 if elided)

=> no unnecessary copies


You could also use pass-by-value for a "setter" method:

void setter_I(string s1, string s2, string s3)
{
m_s1 = move(s1);
m_s2 = move(s2);
m_s3 = move(s3);
}

or pass-by-ref-to-const:

void setter_II(string const& s1, string const& s2, string const& s3)
{
m_s1 = s1;
m_s2 = s2;
m_s3 = s3;
}

But in both cases this might involve some unnecessary copying. In the
first case one string member object might already have enough
resources allocated to hold the new string value. In the second case
you'll have unnecessary copies when the argument was an rvalue.

If you don't want this, the only feasible solution I know of (perfect
forwarding) looks like this:

#include <string>
#include <type_traits>
#include <utility>

#define REQUIRES(...) class=typename \
std::enable_if<(__VAR_ARGS__)>::type

[...]

class ThreeStrings {
[...]
template <class S1, class S2, class S3,
REQUIRES( std::is_convertible<S1,std::string>::value
&& std::is_convertible<S2,std::string>::value
&& std::is_convertible<S3,std::string>::value )>
void setter_III(S1 && s1, S2 && s2, S3 && s3)
{
m_s1 = std::forward<S1>(s1);
m_s2 = std::forward<S2>(s2);
m_s3 = std::forward<S3>(s3);
}
[...]
};

> I don't even know how many overloads would there be for 3 arguments
> (27 maybe?).


That would be 8. Though, still to high for my taste.

> Is there a way to avoid this verbosity?


Try to use pass-by-value and rely on move-optimized types more often.
If for some reason pass-by-value is not an option, perfect forwarding
(see above) is a way to fight the exploding number of otherwise
necessary overloads.

Cheers!
SG
 
Reply With Quote
 
Juha Nieminen
Guest
Posts: n/a
 
      06-24-2010
Sousuke <(E-Mail Removed)> wrote:
> The optimizations that rvalue references make possible are nice, but
> I'm having one problem with them (rvalue refs): they sometimes lead to
> too much verbosity.


If I have understood correctly, you can simply write functions taking
rvalue references only, and skip writing functions taking regular references.
If a function takes an rvalue reference and you call it with an lvalue, the
compiler will still do the right thing, and the function will effectively
work as if it was taking a regular reference instead.

(Although this is just speculation at this point. I haven't actually
studied in detail how these things work in C++0x. So don't quote me on this.
Someone please correct me if I'm wrong here. This is important info to know.)
 
Reply With Quote
 
Paul Bibbings
Guest
Posts: n/a
 
      06-24-2010
"Alf P. Steinbach /Usenet" <(E-Mail Removed)> writes:

> * Sousuke, on 24.06.2010 17:23:
>> The optimizations that rvalue references make possible are nice, but
>> I'm having one problem with them (rvalue refs): they sometimes lead to
>> too much verbosity.
>>
>> When defining a constructor or a setter, you usually take a const T&
>> and assign it to one of the class's members. In C++0x, you can
>> additionally define an overload that takes a T&&:
>>
>> class OneString
>> {
>> public:
>> OneString(const string& s) : m_s(s)
>> {
>> }
>>
>> OneString(string&& s) : m_s(move(s))
>> {
>> }
>>
>> private:
>> string m_s;
>> };
>>
>> One additional overload is not too much verbosity, but see the two-
>> argument case:
>>
>> class TwoStrings
>> {
>> public:
>> TwoStrings(const string& s1, const string& s2) : m_s1(s1),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(const string& s1, string&& s2) : m_s1(s1),
>> m_s2(move(s2))
>> {
>> }
>>
>> TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
>> m_s2(move(s2))
>> {
>> }
>>
>> private:
>> string m_s1;
>> string m_s2;
>> };
>>
>> I don't even know how many overloads would there be for 3 arguments
>> (27 maybe?).
>>
>> Is there a way to avoid this verbosity?

>
> With the disclaimer that I haven't actually used C++0x rvalue
> references, why do you want the ordinary reference overloads? I
> thought much of the point was that an rvalue reference could deal with
> both cases. Isn't it so?


It isn't. Rvalue references add two more implicit definitions to the
C++03 set, which was:

class A {
public:
A(); // ctor
A(const A&); // copy ctor
A& operator=(const A&); // copy assignment
~A(); // destructor
};

so that, in C++0x, you have:

class A {
public:
A(); // ctor
A(const A&); // copy ctor
A(A&&); // move constructor
A& operator=(const A&); // copy assignment
A& operator=(A&&); // move assignment
~A(); // destructor
};

This means that, even if you don't provide the copy constructor or copy
assignment operator, they will be compiler generated as before and will
be the preferred call for lvalues. In essence, the `point' of rvalue
references in this instance is to select the appropriate copy/move ctor
so that an appropriate application of copy/move semantics can be
controlled via the use of lvalue/rvalue refs.

You can see this in the following example:

/cygdrive/d/CPPProjects/CLCPP $cat move_ex.cpp
// file: move_ex.cpp

#include <iostream>
#include <utility>

class A {
public:
A() { std::cout << "ctor\n"; }
A(const A&) { std::cout << "copy ctor\n"; }
A(A&&) { std::cout << "move ctor\n"; } // line 10
A& operator=(const A&) { std::cout << "copy assign\n"; }
A& operator=(A&&) { std::cout << "move assign\n"; }
};

A f(const A& a_ref) {
std::cout << "void f(const A&)\n";
return a_ref;
}

A f(A&& a_rref) {
std::cout << "void f(A&&)\n";
return std::move(a_rref); // line 22
}

int main()
{
A a;
f(a);
f(A());
}

18:21:39 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $gcc -std=c++0x -c move_ex.cpp

18:22:22 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $g++ -static -o move_ex move_ex.o

18:22:33 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $./move_ex
ctor
void f(const A&)
copy ctor
ctor
void f(A&&)
move ctor

In C++0x, of course, you can `delete' the move constructor and, if you
do that in the above code, you will see that it will fail for not being
available.

18:27:49 Paul Bibbings@JIJOU
/cygdrive/d/CPPProjects/CLCPP $gcc -std=c++0x -c move_ex.cpp
move_ex.cpp: In function A f(A&&):
move_ex.cpp:10: error: deleted function A::A(A&&)
move_ex.cpp:22: error: used here

Regards

Paul Bibbings
 
Reply With Quote
 
Paul Bibbings
Guest
Posts: n/a
 
      06-24-2010
"Alf P. Steinbach /Usenet" <(E-Mail Removed)> writes:

> * Sousuke, on 24.06.2010 17:23:
>> The optimizations that rvalue references make possible are nice, but
>> I'm having one problem with them (rvalue refs): they sometimes lead to
>> too much verbosity.
>>
>> When defining a constructor or a setter, you usually take a const T&
>> and assign it to one of the class's members. In C++0x, you can
>> additionally define an overload that takes a T&&:
>>
>> class OneString
>> {
>> public:
>> OneString(const string& s) : m_s(s)
>> {
>> }
>>
>> OneString(string&& s) : m_s(move(s))
>> {
>> }
>>
>> private:
>> string m_s;
>> };
>>
>> One additional overload is not too much verbosity, but see the two-
>> argument case:
>>
>> class TwoStrings
>> {
>> public:
>> TwoStrings(const string& s1, const string& s2) : m_s1(s1),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(string&& s1, const string& s2) : m_s1(move(s1)),
>> m_s2(s2)
>> {
>> }
>>
>> TwoStrings(const string& s1, string&& s2) : m_s1(s1),
>> m_s2(move(s2))
>> {
>> }
>>
>> TwoStrings(string&& s1, string&& s2) : m_s1(move(s1)),
>> m_s2(move(s2))
>> {
>> }
>>
>> private:
>> string m_s1;
>> string m_s2;
>> };
>>
>> I don't even know how many overloads would there be for 3 arguments
>> (27 maybe?).
>>
>> Is there a way to avoid this verbosity?

>
> With the disclaimer that I haven't actually used C++0x rvalue
> references, why do you want the ordinary reference overloads? I
> thought much of the point was that an rvalue reference could deal with
> both cases. Isn't it so?


Of course, in my previous reply, I have *totally* missed the point since
what the OP is referring to are not move constructors at all!

Regards

Paul Bibbings
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Giving an rvalue ref to a function taking an rvalue ref Juha Nieminen C++ 13 08-29-2012 09:25 PM
rvalue references and default constructors Paul Brettschneider C++ 3 04-16-2008 08:12 AM
rvalue references Cory Nelson C++ 3 03-20-2008 11:29 PM
[c++0x] name deduction in overloading with rvalue references Chris Fairles C++ 1 09-03-2007 11:36 PM
operators requiring lvalue/rvalue operands and resulting in rvalue/lvalue Kavya C Programming 9 10-28-2006 01:45 AM



Advertisments