Re: C++0x: unfortunate interaction of rvalue references and concepts
On Dec 7, 10:12 pm, "Joe Smith" <unknown_kev_...@hotmail.com> wrote:
"SG" <s.gesem...@gmail.com> wrote:
The problem is: rvalue references are allowed to bind to lvalues.
Overloading is necessary in this case to distinguish between lvalues
and rvalues. But not all types might be copyable which is why the
first push_back function has an such an additional requirement.
Unfortunately it won't contribute to overload resolution for non-
copyable types. In case of non-copyconstructible types it's possible
to call push_back on lvalues that are possibly destroyed because only
push_back(T&&) is the only function in the overload resolution set.
Really? I was under the impression that a T or a T& (with T not a reference
type) will never bind to a 'T&&'. As I understand it a non-template function
with an argument of type T&& should only be called on temporaries or if a T
has been converted to an rvalue by way of std::move (or some other
mechanism).
When T is not a deduced template parameter, and by current rules, both
lvalues and rvalues (non-const) will bind to a A&& parameter. This is
somewhat similar to the existing rule that both lvalues and rvalues
will bind to a const A& parameter.
It is possible this rule may change prior to standardization.
The current motivation for this rule includes cases such as:
class A
{
...
public:
...
friend ostream& operator<< (ostream&& os, const A& a)
{ return os << ...;}
};
With this one stream inserter (and by current rules) one can use it
like so:
ofstream fs("data");
fs << a;
*and* like so:
ofstream("data") << a;
If we change the rule such that lvalue ostream's can not bind to
ostream&&, then the author of A will have to write two nearly
identical inserters to achieve the same functionality:
class A
{
...
public:
...
friend ostream& operator<< (ostream& os, const A& a)
{ return os << ...;}
friend ostream& operator<< (ostream&& os, const A& a)
{ return os << ...;}
};
I've been asked if there are other cases where it would be convenient
for lvalues to bind to rvalue reference. I don't believe there are
within the standard library. However here is another example using
linear algebra:
Consider a Matrix and Vector class, and functions for factoring the
Matrix in place (Matrixes are often so big you can't afford two of
them), and solving the factored Matrix against a Vector:
void factor(Matrix& m);
Vector solve(const Matrix& m, const Vector& x);
so far so good. This might be used like:
Matrix k = ...
Vector f = ...
factor(k);
Vector x = solve(k, f); // kx == f
Now consider a convenience function which does both the factor and
solve in one call:
Vector factor_and_solve(Matrix&& m, const Vector& x);
In some use cases I will want to form the Matrix in a factory function
and pass it right to factor_and_solve. In these use cases I do not
care about the factored form of the Matrix:
Vector displacement = factor_and_solve(AssembleStiffness(),
ComputeForce());
But in other use cases I will want to save the factored Matrix to
solve against other force vectors (factoring is expensive, solving is
cheap). It would be nice if I could:
Matrix k = AssembleStiffness();
Vector v[3];
v[0] = factor_and_solve(k, ComputeForce(0)); // factor just once,
solve 3 times
v[1] = solve(k, ComputeForce(1));
v[2] = solve(k, ComputeForce(2));
Of course I could work around the problem if k will not bind to
Matrix&&:
Matrix k = AssembleStiffness();
Vector v[3];
factor(k);
v[0] = solve(k, ComputeForce(0));
v[1] = solve(k, ComputeForce(1));
v[2] = solve(k, ComputeForce(2));
And of course I can work around the stream inserter problem by writing
two stream inserters. The trick is figuring out which language rules
are most convenient for the largest number of use cases.
And there will still be times when one must figure some way to bind an
lvalue to an rvalue reference: std::move can't be written without
that capability:
template <RvalueOf T>
RvalueOf<T>::type
move(T&& t);
....
string s1 = ...
string s2 = move(s1);
*Somehow* the move function must bind the s1 to the string&& return
type of move, even if we change the rules to say that lvalues won't
bind to rvalue references. static_cast<string&&>(s1) looks like a
likely way to force that binding.
-Howard
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]