Topic: A question about Rvalue References


Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Sun, 9 Jul 2006 01:31:01 GMT
Raw View
In article <4c2bd$44aaf84e$d99bc06a$16744@news.news-service.com>,
 brok@rubikon.pl (Bronek Kozicki) wrote:

> Howard Hinnant wrote:
> > I actually think this represents a compiler (or proposal) bug as one
> > shouldn't be able to move from a const string.  I would've expected a
> > compile time error resulting from binding a const string to a string&&.
>
> this is indeed worrying.

Not that worrying.  We're not looking at a design defect.  We're looking
at an implementation defect.

I've further investigated this and believe that the correct result is to
fail to compile when passing a const string to string&&.  The C++03
analogy is:

struct A
{
    A();
    A(volatile A&);
    A(const A&);
};

void foo(const A&);

int main()
{
    volatile A a;
    foo(a);
}

The above fails to compile on EDG and gcc 4.x.

error: qualifiers dropped in binding reference of type
          "const A &" to initializer of type "volatile A"
      foo(a);
          ^
However if you change it to:

    foo(A(a));

it compiles.  I.e. the implicit copy constructor isn't tried.

> > Still again, the problem remains that we moved from s.
>
> yes, and I think I know why - std::move is "forced conversion to rvalue".
> What
> if you used std::forward instead?

So, assuming you meant:

    Foo(string&& s1, string&& s2, string&& s3, string&& s4)
        : s1_(std::forward<string>(s1)),
          s2_(std::forward<string>(s2)),
          s3_(std::forward<string>(s3)),
          s4_(std::forward<string>(s4))
        {}

and with the driver:

    string s, s2;
    const string cs;
    Foo f1(s, cs, string(), std::move(s2));

The result should be a failure to compile:

error: qualifiers dropped in binding reference of type
          "string &&" to initializer of type "const string"
      Foo f1(s, cs, string(), std::move(s2));
                ^
If you change the driver to:

    Foo f1(s, string(cs), string(), std::move(s2));

Then it should compile and produce:

string copy
string move
string move
string move
string move

This indicates that "s" got moved from.

Explanation:  All std::forward<A>(a) does is return "a" by A&& (where A
is a non-reference type).  The use case for forward is to correctly
forward a template argument.

> template<class S1, class S2, class S3, class S4>
> Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
> :  m_s1(std::forward<S1>(s1))
> :  m_s2(std::forward<S2>(s2))
> :  m_s3(std::forward<S3>(s3))
> :  m_s4(std::forward<S4>(s4))
> {}

Here S1 is a template type.  If given an lvalue string, S1 will deduce
as a string&.  Then forward<string&> will return s1 by string& &&, which
collapses to just string& (i.e. returns s1 as an lvalue).  If given an
rvalue string, S1 will deduce as string.  Then forward<string> will
return s1 by string&& (an rvalue).  Futhermore, if s1 is a cv-qualified
expression (lvalue or rvalue), those cv-qualifications will be deduced
into the type of S1, and thus passed along as well.

When used with a non-lvalue-reference type, move and forward have
precisely the same behavior:  They both return an rvalue.

When used with a lvalue reference type (in move's case, when passed an
lvalue, move's template parameter will be deduced as an lvalue reference
type), move and forward differ:  move will return an rvalue and forward
will return an lvalue.

In summary, the typical use cases are:

A& lvalue();
A  rvalue();

move(lvalue()); // calls move<A&>,  returns A&&
move(rvalue()); // calls move<A>,   returns A&&

forward won't compile unless the template argument is explicitly
supplied (it can not be deduced):

forward<A&>(lvalue());  // returns A&
forward<A>(rvalue());   // returns A&&

Your use case was:

forward<A>(lvalue());  // returns A&&

I.e. the return type of forward is purely dependent upon the supplied
template argument (which can not be deduced), and not on the ordinary
argument.

I suspect the next question might be:

Q:  What does forward<A&&> do?
A:  It is identical to forward<A>: returns A&&.

-Howard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Mon, 3 Jul 2006 22:09:14 GMT
Raw View
In article <361e7$44a8ca7d$d99bc06a$11666@news.news-service.com>,
 brok@rubikon.pl (Bronek Kozicki) wrote:

> > Sorry, but this won't do it.  Assuming you meant:
> >
> >     Foo(string&& s1, string&& s2, string&& s3, string&& s4)
> >         : s1_(s1),
> >           s2_(s2),
> >           s3_(s3),
> >           s4_(s4)
> >         {}
> >
>
> I'm sorry, this is erroneous assumption. If you use rvalue-references in a
> constructor to employ move construction from actual parameter to sub-object
> being constructed, you need to explicitly "cast" parameter to rvalue using
> std::move. Otherwise, as any other named variable, it's just an lvalue. I
> should have been more explicit before. Constructor initializer list would
> need
> to look like:
>
> Foo(std::string&& s1, std::string&& s2, std::string&& s3, std::string&& s4)
>    : s1_(std::move(s1))
>    , s2_(std::move(s2))
>    , s3_(std::move(s3))
>    , s4_(std::move(s4))
> {}
>
> Obviously, in place of std::string one can use any type that is moveable or
> copyable (or both) and it will still work: non-moveable types will be simply
> copied to sub-objects. IIUC, this is what OP asked for.

Reminder of the driver program:

    string s, s2;
    const string cs;
    Foo f1(s, cs, string(), std::move(s2));

And the result (using a CodeWarrior prototype) is:

string move
string move
string move
string move

I actually think this represents a compiler (or proposal) bug as one
shouldn't be able to move from a const string.  I would've expected a
compile time error resulting from binding a const string to a string&&.
Regardless of this error, if I'm understanding the OP's original
request, the intent was not to move from s (the first parameter).

On second thought, I find myself wondering if a compile-time error is
necessary above.  There exists an implicit conversion function such that
the above would compile:

    Foo f1(s, string(cs), string(), std::move(s2));

which results in:

string copy
string move
string move
string move
string move

Perhaps the compiler-error is not that it bound a const string to a
string&&, but that the copy elision logic kicked in when it shouldn't
have.

Still again, the problem remains that we moved from s.

-Howard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Peter Dimov" <pdimov@gmail.com>
Date: Tue, 4 Jul 2006 12:45:34 CST
Raw View
Howard Hinnant wrote:

> On second thought, I find myself wondering if a compile-time error is
> necessary above.  There exists an implicit conversion function such that
> the above would compile:
>
>     Foo f1(s, string(cs), string(), std::move(s2));

I seem to recall that the C++03 equivalent

struct X
{
  operator X& () const;
};

void f( X& );

int main()
{
  X const cx;
  f( cx );
}

doesn't work (and Comeau C++ 4.3 thinks so, too) but I haven't been
able to find the chapter and verse that states that it won't work. But
maybe invoking string::string( string const& ) to create the target of
string&& is different from invoking X::operator X& () const to create
the target of X&. You never know.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: fgothamNO@SPAM.com (Frederick Gotham)
Date: Tue, 4 Jul 2006 23:24:09 GMT
Raw View
Peter Dimov posted:


> I seem to recall that the C++03 equivalent
>
> struct X
> {
>   operator X& () const;
> };
>
> void f( X& );
>
> int main()
> {
>   X const cx;
>   f( cx );
> }
>
> doesn't work (and Comeau C++ 4.3 thinks so, too) but I haven't been
> able to find the chapter and verse that states that it won't work.


You'd need an illegal const cast:

X::operator X&() const
{
    return const_cast<X&>(*this);
}


--

Frederick Gotham

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Wed, 5 Jul 2006 02:09:10 GMT
Raw View
Howard Hinnant wrote:
> I actually think this represents a compiler (or proposal) bug as one
> shouldn't be able to move from a const string.  I would've expected a
> compile time error resulting from binding a const string to a string&&.

this is indeed worrying.

> Still again, the problem remains that we moved from s.

yes, and I think I know why - std::move is "forced conversion to rvalue". What
if you used std::forward instead?


B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Wed, 5 Jul 2006 02:11:39 GMT
Raw View
In article <1151969431.209174.287730@v61g2000cwv.googlegroups.com>,
 "Peter Dimov" <pdimov@gmail.com> wrote:

> I seem to recall that the C++03 equivalent
>
> struct X
> {
>   operator X& () const;
> };
>
> void f( X& );
>
> int main()
> {
>   X const cx;
>   f( cx );
> }
>
> doesn't work (and Comeau C++ 4.3 thinks so, too) but I haven't been
> able to find the chapter and verse that states that it won't work. But
> maybe invoking string::string( string const& ) to create the target of
> string&& is different from invoking X::operator X& () const to create
> the target of X&. You never know.

<nod> 12.3.2p1 appears to indicate that you can't have a conversion
function to a reference of the same type that is directly called.  I'm
not positive what the rationale is for that.

-Howard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Joe Gottman" <jgottman@carolina.rr.com>
Date: Fri, 30 Jun 2006 21:05:26 CST
Raw View
   I have a question about the rvalue reference proposal
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1855.html).
Suppose I have a class Foo with a constructor that looks like
    Foo::Foo(const string &s1, const string &s2, const string &s3, const
string &s4);

This constructor makes copies of all of its parameters, but, assuming the
rvalue reference proposal is passed, it would be nice to modify it so that
it moves any parameter that is movable and copies all of the others.  Is
there any way to do this under the current proposal without writing 16
overloads, alternating between const string & and string && for each
parameter.

Joe Gottman

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Ion_Gazta=F1aga?=" <igaztanaga@gmail.com>
Date: Sat, 1 Jul 2006 09:02:42 CST
Raw View
Joe Gottman wrote:
> I have a question about the rvalue reference proposal
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1855.html).
> Suppose I have a class Foo with a constructor that looks like
>     Foo::Foo(const string &s1, const string &s2, const string &s3, const
> string &s4);
>
> This constructor makes copies of all of its parameters, but, assuming the
> rvalue reference proposal is passed, it would be nice to modify it so that
> it moves any parameter that is movable and copies all of the others.  Is
> there any way to do this under the current proposal without writing 16
> overloads, alternating between const string & and string && for each
> parameter.

You need to use templates and perfect forwarding. You can know more
about perfect forwarding:

http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Perfect_Forwarding

I think that:

template<class S1, class S2, class S3, class S4>
Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
:  m_s1(std::forward<S1>(s1))
:  m_s2(std::forward<S2>(s2))
:  m_s3(std::forward<S3>(s3))
:  m_s4(std::forward<S4>(s4))
{}

should do the work. Since it's based on templates, a new constructor
will be instantiated for each input combination.

Ion

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Sat, 1 Jul 2006 14:42:47 GMT
Raw View
Joe Gottman wrote:
> Suppose I have a class Foo with a constructor that looks like
>     Foo::Foo(const string &s1, const string &s2, const string &s3, const
> string &s4);
>
> This constructor makes copies of all of its parameters, but, assuming the
> rvalue reference proposal is passed, it would be nice to modify it so that
> it moves any parameter that is movable and copies all of the others.  Is

simply use
Foo::Foo(string &&s1, string &&s2, string &&s3, string &&s4);

assuming that you actually meant std::string, all four would be moveable, as
"moveability" is property of type (the fact that it has move constructor, that
is the one taking non-const rvalue reference, and std::string will have such
construtor if rvalue references are accepted) and not of a single object. Had
you used four different types, one Foo constructor would be still sufficient -
if no move constructor for any of these types is available, regular copy
constructor would be used.


B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Peter Dimov" <pdimov@gmail.com>
Date: Sat, 1 Jul 2006 09:41:08 CST
Raw View
Joe Gottman wrote:
> I have a question about the rvalue reference proposal
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1855.html).
> Suppose I have a class Foo with a constructor that looks like
>     Foo::Foo(const string &s1, const string &s2, const string &s3, const
> string &s4);
>
> This constructor makes copies of all of its parameters, but, assuming the
> rvalue reference proposal is passed, it would be nice to modify it so that
> it moves any parameter that is movable and copies all of the others.  Is
> there any way to do this under the current proposal without writing 16
> overloads, alternating between const string & and string && for each
> parameter.

One way that comes to mind is to use pass by value:

Foo::Foo( string s1, string s2, string s3, string s4 );

and then std::move from s1-s4 in the constructor body. If my impression
is correct, currently the prevalent thinking is that a compiler should
be allowed to move whenever it's allowed to perform copy ellision, so
if some of the arguments are rvalues, they would be moved into the
corresponding parameter (if in-place construction isn't possible for
some reason.)

It would be (very) nice if the compiler could insert the std::move
calls by itself in the common situation of

Foo::Foo( string s1, string s2, string s3, string s4 ):
 s1_( s1 ), s2_( s2 ), s3_( s3 ), s4_( s4 ) {}

I'm not sure whether the current wording allows that, though. The
blanket "as if" rule doesn't cover the case since it isn't easy to
prove that moving from s1 doesn't affect s2.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: jgottman@carolina.rr.com ("Joe Gottman")
Date: Sun, 2 Jul 2006 17:50:55 GMT
Raw View
"Ion Gazta=F1aga" <igaztanaga@gmail.com> wrote in message=20
news:1151746221.228292.252790@j8g2000cwa.googlegroups.com...
> Joe Gottman wrote:
>> I have a question about the rvalue reference proposal
>> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1855.html).
>> Suppose I have a class Foo with a constructor that looks like
>>     Foo::Foo(const string &s1, const string &s2, const string &s3, con=
st
>> string &s4);
>>
>> This constructor makes copies of all of its parameters, but, assuming =
the
>> rvalue reference proposal is passed, it would be nice to modify it so=20
>> that
>> it moves any parameter that is movable and copies all of the others.  =
Is
>> there any way to do this under the current proposal without writing 16
>> overloads, alternating between const string & and string && for each
>> parameter.
>
> You need to use templates and perfect forwarding. You can know more
> about perfect forwarding:
>
> http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Perf=
ect_Forwarding
>
> I think that:
>
> template<class S1, class S2, class S3, class S4>
> Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
> :  m_s1(std::forward<S1>(s1))
> :  m_s2(std::forward<S2>(s2))
> :  m_s3(std::forward<S3>(s3))
> :  m_s4(std::forward<S4>(s4))
> {}
>
> should do the work. Since it's based on templates, a new constructor
> will be instantiated for each input combination.


   Suppose that the constructor does a lot of work, so I don't want to=20
define it inline.  With constructor delegation going into the standard (s=
ee=20
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1895.pdf), I cou=
ld=20
probably define the constructor for one combination and use a template to=
=20
delegate to it in all the other cases.  Which constructor would I be bett=
er=20
off delegating to?  The one with all const references, or the one with al=
l=20
rvalue references?

Joe Gottman=20

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Sun, 2 Jul 2006 17:56:55 GMT
Raw View
In article <VNipg.26427$R26.14406@tornado.southeast.rr.com>,
 "Joe Gottman" <jgottman@carolina.rr.com> wrote:

>    I have a question about the rvalue reference proposal
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1855.html).
> Suppose I have a class Foo with a constructor that looks like
>     Foo::Foo(const string &s1, const string &s2, const string &s3, const
> string &s4);
>
> This constructor makes copies of all of its parameters, but, assuming the
> rvalue reference proposal is passed, it would be nice to modify it so that
> it moves any parameter that is movable and copies all of the others.  Is
> there any way to do this under the current proposal without writing 16
> overloads, alternating between const string & and string && for each
> parameter.

For entertainment purposes I have implemented the various suggestions
you received and instrumented them with "string move" or "string copy"
using a a fake string:

struct string
{
    string() {}
    string(const string&) {std::cout << "string copy\n";}
    string(string&&) {std::cout << "string move\n";}
};

The driver program constructs the Foo with a variety of const/non-const,
lvalue/rvalue arguments:

int main()
{
    string s, s2;
    const string cs;
    Foo f1(s, cs, string(), std::move(s2));
}

In article <1151746221.228292.252790@j8g2000cwa.googlegroups.com>,
 "Ion Gaztaaga" <igaztanaga@gmail.com> wrote:

> I think that:
>
> template<class S1, class S2, class S3, class S4>
> Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
> :  m_s1(std::forward<S1>(s1))
> :  m_s2(std::forward<S2>(s2))
> :  m_s3(std::forward<S3>(s3))
> :  m_s4(std::forward<S4>(s4))
> {}

results in:

string copy
string copy
string move
string move

which is the best that can be expected.

In article <1151763036.920453.317460@p79g2000cwp.googlegroups.com>,
 "Peter Dimov" <pdimov@gmail.com> wrote:

> Foo::Foo( string s1, string s2, string s3, string s4 );
>
> and then std::move from s1-s4 in the constructor body.

    Foo(string s1, string s2, string s3, string s4)
        : s1_(std::move(s1)),
          s2_(std::move(s2)),
          s3_(std::move(s3)),
          s4_(std::move(s4))
        {}

results in:

string move
string copy
string copy
string move
string move
string move
string move

which has the same number of copies as Ion's solution, is arguably
easier to code, but has 5 moves vs 2 (whether or not that is significant
probably depends on several factors including the implementation of
string and the context of Foo).

In article <1151763036.920453.317460@p79g2000cwp.googlegroups.com>,
 "Peter Dimov" <pdimov@gmail.com> wrote:

> It would be (very) nice if the compiler could insert the std::move
> calls by itself in the common situation of
>
> Foo::Foo( string s1, string s2, string s3, string s4 ):
>  s1_( s1 ), s2_( s2 ), s3_( s3 ), s4_( s4 ) {}
>
> I'm not sure whether the current wording allows that, though. The
> blanket "as if" rule doesn't cover the case since it isn't easy to
> prove that moving from s1 doesn't affect s2.

<nod> Right. If you can talk the language folk into eliding the copy
construction here, then we'd be in business.  Otherwise there is a
general ban on implicitly moving from lvalues (anything with a name).

In article <d2373$44a681ac$d99bc06a$19322@news.news-service.com>,
 brok@rubikon.pl (Bronek Kozicki) wrote:

> simply use
> Foo::Foo(string &&s1, string &&s2, string &&s3, string &&s4);

Sorry, but this won't do it.  Assuming you meant:

    Foo(string&& s1, string&& s2, string&& s3, string&& s4)
        : s1_(s1),
          s2_(s2),
          s3_(s3),
          s4_(s4)
        {}

This produces:

string copy
string copy
string copy
string copy

If the variable has a name, it is an lvalue, and thus won't be
implicitly moved from (except in the rare cases where copy elision is
already allowed).

-Howard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Ion_Gazta=F1aga?=" <igaztanaga@gmail.com>
Date: Sun, 2 Jul 2006 18:48:00 CST
Raw View
> In article <1151746221.228292.252790@j8g2000cwa.googlegroups.com>,
>  "Ion Gaztaaga" <igaztanaga@gmail.com> wrote:
>
> > I think that:
> >
> > template<class S1, class S2, class S3, class S4>
> > Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
> > :  m_s1(std::forward<S1>(s1))
> > :  m_s2(std::forward<S2>(s2))
> > :  m_s3(std::forward<S3>(s3))
> > :  m_s4(std::forward<S4>(s4))
> > {}
>
> results in:
>
> string copy
> string copy
> string move
> string move
>
> which is the best that can be expected.

Don't forget the destructors :-) This approach has, (correct me if I'm
wrong) another advantage: you can construct for convertible values:

Foo a ("one", "two", "three", "four");

is optimal code, and should call only four string constructions. On the
other hand:

Foo::Foo(string s1, string 2, string s3, string s4)
:  m_s1(std::move(s1))
,  m_s2(std::move(s2))
,  m_s3(std::move(s3))
,  m_s4(std::move(s4))
{}

Would call 4 string constructions + 4 moves + 4 moved string
destructions. However, type checking is stronger, because if a make a
mistake and I pass an integer, the error is produced in the user's
code:

//Can't convert from int to string
Foo a (int(0), "two", "three", "four");

whereas with my approach, the error is generated in the constructor
body. In code where we don't want conversion features and we want to
minimize instantiations, Peter's approach is better.

Regards,

Ion

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "=?iso-8859-1?q?Ion_Gazta=F1aga?=" <igaztanaga@gmail.com>
Date: Sun, 2 Jul 2006 18:47:20 CST
Raw View
> > I think that:
> >
> > template<class S1, class S2, class S3, class S4>
> > Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
> > :  m_s1(std::forward<S1>(s1))
> > :  m_s2(std::forward<S2>(s2))
> > :  m_s3(std::forward<S3>(s3))
> > :  m_s4(std::forward<S4>(s4))
> > {}
> >
> > should do the work. Since it's based on templates, a new constructor
> > will be instantiated for each input combination.
>
>
>    Suppose that the constructor does a lot of work, so I don't want to
> define it inline.  With constructor delegation going into the standard (see
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1895.pdf), I could
> probably define the constructor for one combination and use a template to
> delegate to it in all the other cases.  Which constructor would I be better
> off delegating to?  The one with all const references, or the one with all
> rvalue references?

You can't delegate the constructor because the paper says:

"At most one other constructor may be named as the target constructor.
If a sibling constructor is named in the initializer list, then the
initializer list shall contain nothing else (i.e., no other base or
member initializers are allowed). The target constructor is selected by
overload resolution and template argument deduction, as usual."

If you need to do some work _after_ initializing the values, you must
call a member function:

template<class S1, class S2, class S3, class S4>
Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
//first assign
:  m_s1(std::forward<S1>(s1))
,  m_s2(std::forward<S2>(s2))
,  m_s3(std::forward<S3>(s3))
,  m_s4(std::forward<S4>(s4))
{
   this->hard_work();
}

This approach minimizes move operations. If moving is very cheap, maybe
Peter's alternative is better, since it's simpler to code and has no
sensible performance impact.

If you need to do a lot of work before assigning the values, you can
delegate constructor:

template<class S1, class S2, class S3, class S4>
Foo::Foo(S1 &&s1, S2 &&s2, S3 &&s3, S4 &&s4)
//Hard work
:  Foo()
{
   //Now move
   m_s1(std::forward<S1>(s1))
   m_s2(std::forward<S2>(s2))
   m_s3(std::forward<S3>(s3))
   m_s4(std::forward<S4>(s4))
}

But you are initializing the strings at least _twice_: first in the in
the delegated constructor, and after that, in the body.

The key is that rvalueness is not a runtime information, but a
compile-time information, so you can have a single non-templated code
that works optimally for both lvalue and rvalue values only if copying
is the same as moving (for example, POD types). Otherwise, you have to
choose:

-> Templated code.
-> Peter's approach: catch by value, and move it. This is acceptable if
you have to move the value anyway and moving is cheap.

Regards,

Ion

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Peter Dimov" <pdimov@gmail.com>
Date: Sun, 2 Jul 2006 18:46:17 CST
Raw View
Howard Hinnant wrote:

> In article <1151763036.920453.317460@p79g2000cwp.googlegroups.com>,
>  "Peter Dimov" <pdimov@gmail.com> wrote:
>
> > Foo::Foo( string s1, string s2, string s3, string s4 );
> >
> > and then std::move from s1-s4 in the constructor body.
>
>     Foo(string s1, string s2, string s3, string s4)
>         : s1_(std::move(s1)),
>           s2_(std::move(s2)),
>           s3_(std::move(s3)),
>           s4_(std::move(s4))
>         {}
>
> results in:
>
> string move
> string copy
> string copy
> string move
> string move
> string move
> string move
>
> which has the same number of copies as Ion's solution, is arguably
> easier to code, but has 5 moves vs 2 (whether or not that is significant
> probably depends on several factors including the implementation of
> string and the context of Foo).

I opted to interpret the initial question as having the implicit
constraint that the constructor cannot be made a template (not all Foos
are shipped with source) as it makes it much more interesting. :-)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Mon, 3 Jul 2006 15:29:03 GMT
Raw View
Howard Hinnant wrote:
> In article <d2373$44a681ac$d99bc06a$19322@news.news-service.com>,
>  brok@rubikon.pl (Bronek Kozicki) wrote:
>
>> simply use
>> Foo::Foo(string &&s1, string &&s2, string &&s3, string &&s4);
>
> Sorry, but this won't do it.  Assuming you meant:
>
>     Foo(string&& s1, string&& s2, string&& s3, string&& s4)
>         : s1_(s1),
>           s2_(s2),
>           s3_(s3),
>           s4_(s4)
>         {}
>

I'm sorry, this is erroneous assumption. If you use rvalue-references in a
constructor to employ move construction from actual parameter to sub-object
being constructed, you need to explicitly "cast" parameter to rvalue using
std::move. Otherwise, as any other named variable, it's just an lvalue. I
should have been more explicit before. Constructor initializer list would need
to look like:

Foo(std::string&& s1, std::string&& s2, std::string&& s3, std::string&& s4)
   : s1_(std::move(s1))
   , s2_(std::move(s2))
   , s3_(std::move(s3))
   , s4_(std::move(s4))
{}

Obviously, in place of std::string one can use any type that is moveable or
copyable (or both) and it will still work: non-moveable types will be simply
copied to sub-objects. IIUC, this is what OP asked for.

You will find good introduction to rvalue references here:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html


B.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]