繁体   English   中英

对左值和右值重复使用代码

[英]Re-using code for lvalues and rvalues

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

现在,我还有一个移动构造函数,它基本上可以使用与复制构造函数相同的函数层次结构。 这将起作用,因为我可以将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之前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()来传递右值引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM