将rvalue引用绑定到给定对象或其临时副本的最佳方法是什么?
A &&var_or_dummy = modify? static_cast<A&&>( my_A )
: static_cast<A&&>( static_cast<A>( my_A ) );
(此代码不适用于我最近的GCC 4.6 ...我记得之前有效,但现在它总是返回副本。)
在第一行,static_cast
将my_A
从左值变换为x值。 (C ++0x§5.2.9/ 1-3)第二行的内部static_cast
执行左值到右值的转换,外部的const
从该prvalue获得xvalue。
这似乎得到支持,因为命名引用有条件地绑定到临时符合§12.2/ 5。同样的技巧在C ++ 03中使用A &&var_or_dummy = modify? std::move( my_A )
: static_cast<A&&>( A( my_A ) );
引用的方式相同。
我也可以不那么冗长地写同样的东西:
move
现在它要短得多。第一个缩写是有问题的:move
应该表示对象发生了某些事情,而不是仅仅是左值到左右值的左值。令人困惑的是,:
之后无法使用A(my_A)
,因为函数调用会中断临时到引用的绑定。语法static_cast
可能比A &&var_or_dummy = modify? (A&&)( my_A ) : (A&&)( A( my_A ) );
更清晰,但它在技术上等同于C风格的演员。
我也可以一路走下去,完全用C风格的演员阵容写出来:
static_cast
毕竟,如果这是一个习惯用语,那一定很方便,my_A
并没有真正保护我免受任何事情的影响 - 真正的危险是无法直接绑定到true
在A
案例中。
另一方面,这很容易被重复三次的typename占据主导地位。如果将V
替换为一个大而丑陋的模板ID,我真的想要一个真正的捷径。
(请注意#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 ) ) )
只评估一次,尽管出现了五次:)
{{1}}像pros一样哈克,我认为这是最好的选择。它有点危险,因为它返回一个xvalue,所以它不应该在引用初始化之外使用。
必须有一些我没想过的东西...建议?
答案 0 :(得分:2)
我发现你的方法存在两个问题。
你依赖行为
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?
目前的标准草案N3126包含5.16 / 4:
如果[到条件运算符]的第二个和第三个操作数是相同值类别的glvalues并且具有相同的类型,则结果是该类型和值类别
这让我觉得上面两个断言应该成立。但是使用GCC 4.5.1,第二个失败了。我相信这是一个海湾合作委员会的错误。
此外,您依赖编译器来延长以下示例中引用的临时对象y
的生命周期:
A func();
A&& x = func(); // #1
A&& y = static_cast<A&&>(func()); // #2
x
不会是悬挂式参考,但我对。y
不太确定。我认为关于延长临时生命周期的规则只适用于初始化表达式是纯 rvalues的情况。至少,这将大大简化实施。此外,海湾合作委员会似乎同意我的看法。在第二种情况下,GCC不会延长临时A对象的生命周期。这将是您的方法中的悬空参考问题
更新:根据12.2 / 5,在两种情况下,临时对象的生命周期都应该延长,#1和#2。例外列表中的所有要点似乎都不适用于此处。同样,海湾合作委员会在这方面似乎也有错误。
解决问题的一个简单方法是:
vector<A> tempcopy;
if (!modify) tempcopy.push_back(myA);
A& ref = modify ? myA : tempcopy.back();
另外,你可以使用boost :: scoped_ptr而不是vector。
答案 1 :(得分:2)
通过额外的函数调用来避免这一切:
void f(bool modify, A &obj) {
return [&](A &&obj) {
real();
work();
}(modify ? std::move(obj) : std::move(A(obj)));
}
而不是:
void f(bool modify, A &obj) {
A &&var_or_dummy = /* ??? */;
real();
work();
}
答案 2 :(得分:0)
xvalue安全问题可以通过提供在表达式内使用的替代方法来解决。问题完全不同,现在我们不想要xvalue结果并且可以使用函数:
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 ) );
现在类型必须是默认构造的,并且总是构造虚拟。有条件地评估该副本。我不认为我真的想要处理过多的代码。
Boost Optional可以帮助一点;它只需要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;
}
可选很有用,但它与C ++ 0x联合有一些重叠。重新实现并不太难。
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;
}
optional_union
类仅适用于此应用程序......显然它可以扩展很多。