What is the best way to bind an rvalue reference to either a given object or a temporary copy of it?
A &&var_or_dummy = modify? static_cast<A&&>( my_A )
: static_cast<A&&>( static_cast<A>( my_A ) );
(This code doesn't work on my recent GCC 4.6… I recall it working before, but now it always returns a copy.)
On the first line, the static_cast
transforms my_A
from an lvalue to an xvalue. (C++0x §5.2.9/1-3) The inner static_cast
on the second line performs lvalue-to-rvalue conversion, and the outer one obtains an xvalue from this prvalue.
This appears to be supported because the named reference is conditionally bound to the temporary per §12.2/5. The same trick works the same way in C++03 with a const
reference.
I can also write the same thing less verbosely:
A &&var_or_dummy = modify? std::move( my_A )
: static_cast<A&&>( A( my_A ) );
Now it's much shorter. The first abbreviation is questionable: move
is supposed to signal that something is happening to the object, not a mere lvalue-to-xvalue-to-lvalue shuffle. Confusingly, move
cannot be used after the :
because the function call would interrupt the temporary-to-reference binding. The syntax A(my_A)
is perhaps clearer than the static_cast
, but it's technically equivalent to a C-style cast.
I can also go all the way and write it entirely in C-style casts:
A &&var_or_dummy = modify? (A&&)( my_A ) : (A&&)( A( m开发者_如何学编程y_A ) );
After all, if this is going to be an idiom, it must be convenient, and static_cast
isn't really protecting me from anything anyway — the real danger is failing to bind directly to my_A
in the true
case.
On the other hand, this easily gets dominated by the typename repeated three times. If A
were replaced with a big, ugly template-id, I'd really want a real shortcut.
(Note that V
is evaluated only once despite appearing five times:)
#define VAR_OR_DUMMY( C, V ) ( (C)? \
static_cast< typename std::remove_reference< decltype(V) >::type && >( V ) \
: static_cast< typename std::remove_reference< decltype(V) >::type && > ( \
static_cast< typename std::remove_reference< decltype(V) >::type >( V ) ) )
Hackish as macros are, I think that's the best alternative of the bunch. It's a bit dangerous because it returns an xvalue, so it shouldn't be used outside reference initialization.
There must be something I haven't thought of… suggestions?
Just avoid this whole mess with an extra function call:
void f(bool modify, A &obj) {
return [&](A &&obj) {
real();
work();
}(modify ? std::move(obj) : std::move(A(obj)));
}
Instead of:
void f(bool modify, A &obj) {
A &&var_or_dummy = /* ??? */;
real();
work();
}
It's lambdas, lambdas, everywhere!
I see two issues with your approach.
You rely on the behaviour
int i = 0;
int& j = true? i : i;
int&& k = true? move(i) : move(i);
assert(&i == &j); // OK, Guaranteed since C++98
assert(&i == &k); // Does this hold as well?
The current standard draft N3126 contains 5.16/4:
If the second and third operands [to the conditional operator] are glvalues of the same value category and have the same type, the result is of that type and value category
which makes me think that the above two asserts should hold. But using GCC 4.5.1 the second one fails. I believe that this is a GCC bug.
In addition, you rely on the compiler to extend life time of the temporary object y
refers to in the following example:
A func();
A&& x = func(); // #1
A&& y = static_cast<A&&>(func()); // #2
x
won't be a dangling reference but I'm not so sure about .y
. I think the rule about extending the life-time of temporaries is only supposed to apply in cases the initializer expressions are pure rvalues. At least, this would greatly simplify the implementation. Also, GCC seems to agree with me on this one. GCC doesn't extend the life-time of the temporary A object in the second case. This would be a dangling-reference problem in your approach
Update: According to 12.2/5 the lifetimes of the temporary objects are supposed to be extended in both cases, #1 and #2. None of the bullet points in the list of exceptions seem to apply here. Again, GCC seems to be buggy in this regard.
One easy solution for your problem would be:
vector<A> tempcopy;
if (!modify) tempcopy.push_back(myA);
A& ref = modify ? myA : tempcopy.back();
Alternativly, you could use a boost::scoped_ptr instead of a vector.
The issue of xvalue safety can be worked around somewhat by providing an alternative for use inside expressions. The issues are completely different, now we don't want an xvalue result and can use a function:
template< typename T >
T &var_or_dummy( bool modify, T &var, T &&dummy = T() ) {
if ( modify ) return var;
else return dummy = var;
}
maybe_get_result( arg, var_or_dummy( want_it, var ) );
Now the type has to be default-constructible, and the dummy is always constructed. The copy is conditionally evaluated. I don't think I'd really want to deal with code that did too much of this.
Boost Optional can help a bit; it only requires CopyConstructible T:
template< typename T >
T &var_or_dummy( bool modify, T &var,
boost::optional< T > &&dummy = boost::optional< T >() ) {
if ( modify ) return var;
else return dummy = var;
}
Optional is useful, but it has some overlap with C++0x unions. It's not too hard to reimplement.
template< class T >
struct optional_union {
bool valid;
union storage {
T obj; // union of one non-POD member simply reserves storage
storage() {} // uh, what could the constructor/destructor possibly do??
~storage() {}
} s;
optional_union() : valid( false ) {}
optional_union &operator=( T const &in ) {
new( &s.obj ) T( in ); // precondition: ! valid
valid = true;
return *this;
}
~optional_union()
{ if ( valid ) s.obj.~T(); }
};
template< typename T >
T &var_or_dummy( bool modify, T &var,
optional_union< T > &&dummy = optional_union< T >() ) {
if ( modify ) return var;
else return ( dummy = var ).s.obj;
}
The optional_union
class is only sufficient for this application… obviously it could be expanded a lot.
精彩评论