绑定对象或虚拟引用的样式

时间:2010-10-09 05:32:47

标签: c++ coding-style c++11 rvalue-reference

将rvalue引用绑定到给定对象或其临时副本的最佳方法是什么?

A &&var_or_dummy = modify? static_cast<A&&>( my_A )
                         : static_cast<A&&>( static_cast<A>( my_A ) );

(此代码不适用于我最近的GCC 4.6 ...我记得之前有效,但现在它总是返回副本。)

在第一行,static_castmy_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并没有真正保护我免受任何事情的影响 - 真正的危险是无法直接绑定到trueA案例中。

另一方面,这很容易被重复三次的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,所以它不应该在引用初始化之外使用。

必须有一些我没想过的东西...建议?

3 个答案:

答案 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();
}

这是lambdas, lambdas, everywhere

答案 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类仅适用于此应用程序......显然它可以扩展很多。