重用左值和右值的代码

Re-using code for lvalues and rvalues

本文关键字:代码      更新时间:2023-10-16

假设我有一个复制构造函数。此构造函数调用一个函数层次结构,将复制的对象作为左值引用传递。

现在,我还有一个move构造函数,它基本上可以使用与复制构造函数相同的函数层次结构。这是可行的,因为我可以将rvalue参数传递给左值层次结构。

但是在层次结构的某个地方,我有一个函数,它会在左值情况下复制资源,并在右值情况下"窃取"资源。

是否有一种方法来决定,传递给该函数的左值引用是否来自右值?我想没有。或者,通常的方法是什么,当你有一个用于复制和移动结构的函数层次结构时,它们只在很少的函数中有所不同?

代码示例:

class A{
    A(const A& a){
        initFrom(a);  
    }
    A(A&& a){
        initFrom(a); 
    }
    void initFrom(const A& a){
       // call a hierarchy of functions, of which one of them calls initResource(const A&)
    }
    void initResource(const A& a){
       if(a == rvalue reference){ // **** Here's the question... ****
           // steal resource
           this->ptr = a.ptr;
           a.ptr = nullptr;
       }
       else{
           // copy resource
           this->ptr = allocate...
           copy from a.ptr to this->ptr
       }
    }  

这是一个完美转发的典型例子:

template <typename T>
A(T && t) { initFrom(std::forward<T>(a)); }
template <typename T>
void initFrom(T && t)
{
    // other calls
    initResource(std::forward<T>(t));
}
void initResource(A const & rhs) { /* copy from rhs */ }
void initResource(A && rhs)      { /* move from rhs */ }

(似乎你应该要么能够合并initFrom到构造函数,否则你的类可能试图做太多,你应该重构成单一责任的组件。)

这里的一种替代方法是修改initFrom以接受"通用引用"以允许引用崩溃,然后使用std::forward进行完美转发。然后,您可能需要重新考虑剩余的调用层次结构。

class A{
    A(const A& a){
        initFrom(a);
    }
    A(A&& a){
        initFrom(a);
    }
    template <typename B>
    void initFrom(B&& a){ // reference collapsing applies
      // call a hierarchy of functions, of which one of them calls initResource(const A&)
      initResource(std::forward<B>(a));
    }
    void initResource(A&& a){
      // steal resource
      this->ptr = a.ptr;
      a.ptr = nullptr;
    }
    void initResource(const A& a){
      // copy resource
      this->ptr = allocate...
      //copy from a.ptr to this->ptr
    }
};

我认为一个更简单的替代方法是在initFrom调用之前首先将资源"移动"到您的类中。

    A(A&& a){
        this->ptr = a.ptr;
        a.ptr = nullptr;
        initFrom(a);
    }

根据您的调用层次结构和所有这些函数除了传递对象之外必须做的事情,如果您打算将对象存储在您的类中,您可能会使用另一种技术。

class A {
    A(const A& a) {
        initFrom(A(a)); // take a copy here
    }
    A(A&& a) {
        initFrom(std::move(a)); // move here
    }
    void initFrom(A&& a) { 
        initResource(std::move(a)); // just pass down
    }
    void initResource(A&& a) {
        // you already have your copy of a here that you can store completely
        // or take its guts
    }

这样,你只需要一次实现所有的方法(对于右值引用),无论移动还是获取副本都直接在方法调用中处理。注意,必须始终使用std::move()来传递右值引用。