当 C++ 函数不 'move from' 右值引用参数时,这是合理的方法吗?

Is it reasonable approach when C++ function does not 'move from' rvalue reference argument?

假设 class Object 满足 MoveConstructibleMoveAssignable 的要求,但不满足 的要求]CopyConstructibleCopyAssignable(即包含 unique_ptr 的 PIMPL)。然后我有两个消费者:

 bool consumer1(Object && arg) {
       if(arg ...) { // arg satisfies some condition
          Object b {std::move(arg)};
          // some processing
          return true;
        }
        return false;
 }

 bool consumer2(Object &&arg) {};

 int main() {
     Object arg;
     // some arg initialization here
     if(!consumer1(std::move(arg))) 
        consumer2(std::move(arg));
 }

即我想 Object 的一个实例移动到consumer1(),如果它由于某种原因不接受它,我将实例移动到consumer2()。

或者,我可以检查实例的状态(即它是否被移动)而不是依赖 return 值。

虽然我知道that

std::move doesn’t move anything.

不过两个动作我还是觉得不舒服

这是一个合理的做法吗?有没有更简单的方法来实现相同的想法?

如您所写,当 1 returns 为 false 时,arg 仍处于有效状态,因此您的代码没有任何问题(即使它不是' t,还是不会有什么问题的)。

这个问题比较主观,但我会尽量给出明确的回答。

虽然您编写的代码技术上没有任何错误,因为代码将编译并且 运行 正确,来自 higher-level这是 design-smell.

对对象 std::move 的明显重复调用限制了开发人员推断该对象生命周期的能力,这会降低可读性。静态分析工具也可能将此类移动标记为“use-after-move”,尽管事实上该对象并未实际就位(从技术上讲,这不会是错误的)。

我想到了几个清理选项,但完全是可选的,并且取决于您的实际 use-case:

  1. std::move 的条件分解为单独的检查,或者
  2. 使用后备模式

打破条件

如果您可以突破 consumer1 消费 arg 的条件,那么您可以在首次尝试消费之前明确测试这一点——允许您有两个明显不同的分支 [= =16=]独立:

if(can_consume(arg)) {
    consumer1(std::move(arg));
} else {
    consumer2(std::move(arg));
} 

回退模式

另一种可能的重构是使用回退模式,在这种模式下,您设计的代码可以在失败时发生 dependency-injected“回退”。

如果这都是简单的原始函数,一个简单的例子可以是:

using FallbackFunction = void(*)(Object&&);

void consume1_or(Object&& arg, FallbackFunction function)
{
    if(arg ...) { // arg satisfies some condition
        Object b {std::move(arg)};
        // some processing
        return
    }
    // fallback on failure
    (*function)(std::move(arg));
}

...

// use
consume1_or(std::move(arg), &consume2);

然而,这也可以扩展到一个完整的接口,其中扩展 Consumer 的东西在构造时也接受 Consumer 以在失败时将对象传递给。