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

发布于 2024-09-26 22:04:09 字数 1771 浏览 8 评论 0原文

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

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 执行左值到右值的转换,外部从该纯右值获取 xvalue。

这似乎是受支持的,因为根据 §12.2/5,命名引用有条件地绑定到临时引用。同样的技巧在 C++03 中使用 const 引用以相同的方式工作。

我也可以写得更简洁:

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

现在它更短了。第一个缩写是有问题的: move 应该表示对象正在发生某些事情,而不仅仅是左值到x值到左值的洗牌。令人困惑的是,move 不能在 : 之后使用,因为函数调用会中断临时到引用的绑定。语法 A(my_A) 可能比 static_cast 更清晰,但它在技术上相当于 C 风格的强制转换。

我也可以一路走下去,完全用 C 风格的强制转换来编写它:

A &&var_or_dummy = modify? (A&&)( my_A ) : (A&&)( A( my_A ) );

毕竟,如果这将成为一个习惯用法,那么它一定很方便,并且 static_cast 并不能真正保护我免受无论如何,真正的危险是在 true 情况下无法直接绑定到 my_A

另一方面,这很容易被重复三次的类型名所支配。如果 A 被替换为一个又大又丑的模板 ID,我真的想要一个真正的快捷方式。

(请注意,尽管 V 出现了五次,但仅计算一次:)

#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 ) ) )

虽然宏很黑客,但我认为这是最好的选择。它有点危险,因为它返回一个 xvalue,因此不应在引用初始化之外使用它。

一定有什么我没有想到的……建议?

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( my_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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

双手揣兜 2024-10-03 22:04:09

只需通过额外的函数调用来避免整个混乱:

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

它是 lambda,lambda,无处不在

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!

温柔少女心 2024-10-03 22:04:09

我发现你的方法有两个问题。

您依赖的行为

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:

如果[条件运算符]的第二个和第三个操作数是相同值类别且具有相同类型的泛左值,则结果就是该类型和值类别

这使我认为上述两个断言应该成立。但是使用 GCC 4.5.1 第二个失败了。我相信这是一个 GCC 错误。

此外,您还可以依靠编译器来延长 y 在以下示例中引用的临时对象的生命周期:

A func();

A&& x = func();                   // #1
A&& y = static_cast<A&&>(func()); // #2

x 不会是悬空引用 但我不太确定y。我认为延长临时变量生命周期的规则仅适用于初始化表达式是纯右值的情况。至少,这将大大简化实施。另外,海湾合作委员会似乎在这一点上同意我的观点。在第二种情况下,GCC 不会延长临时 A 对象的生命周期。这将是您的方法中的悬空引用问题

更新:根据 12.2/5,临时对象的生命周期在 #1 和 #2 两种情况下都应该延长。例外列表中的要点似乎都不适用于此。同样,GCC 在这方面似乎也存在缺陷。

解决您的问题的一个简单解决方案是:

vector<A> tempcopy;
if (!modify) tempcopy.push_back(myA);
A& ref = modify ? myA : tempcopy.back();

或者,您可以使用 boost::scoped_ptr 而不是向量。

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.

小兔几 2024-10-03 22:04:09

通过提供在表达式内部使用的替代方案,可以在一定程度上解决 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 ) );

现在类型必须是默认可构造的,并且始终会构造虚拟值。该副本是有条件评估的。我认为我真的不想处理做太多这样的事情的代码。

BoostOptional 可以提供一些帮助;它只需要 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 很有用,但它与 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 类仅足以满足此应用程序......显然它可以扩展很多。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文