重用左值和右值的代码

时间:2014-07-24 09:17:58

标签: c++ c++11 move-semantics rvalue-reference

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

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

但是在层次结构中的某个地方,我有一个函数可以复制左值框中的资源并且“偷”' 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
       }

    }  

3 个答案:

答案 0 :(得分:2)

这是完美转发的典型示例:

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合并到构造函数中,否则你的类可能会尝试做太多而你应该将它重构为单一责任组件。)

答案 1 :(得分:1)

此处的一种替代方法是修改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);
    }

但你在这里的里程可能会有所不同。

答案 2 :(得分:1)

根据您的调用层次结构以及除了传递对象之外所有这些函数必须执行的操作,如果您的目标是将对象存储在类中,则可以使用其他技术。

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()来传递右值参考。