译文

VC10中的C++0x特性 Part 2 (3) : 右值引用

翻译:飘飘白云 | 2009-06-01 14:44:18 | 阅读403 | 来源

VC10中的C++0x特性 Part 2 (3) : 右值引用

来源:vcblog 翻译:飘飘白云 kesalin@gmail.com

这个系列的第一部分( 123 )介绍了 lambda, auto static_assert

第二部分共五页:  第一页  第二页  本页 第四页 第五页

 

move 语意:从 lvalue 移动

现在,如果你喜欢用拷贝赋值函数来实现你的拷贝构造函数该怎样做呢,那你也可能试图用 move 拷贝赋值函数来实现 move 构造函数。这样作是可以的,但是你得小心。下面就是一个错误的实现:

 

C:Temp>type unified_wrong.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        m_p = NULL;

        *this = other;

    }

 

#ifdef MOVABLE

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = NULL;

        *this = other; // WRONG

    }

#endif // #ifdef MOVABLE

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

#ifdef MOVABLE

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

#endif // #ifdef MOVABLE

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

remote_integer frumple(const int n) {

    if (n == 1729) {

        return remote_integer(1729);

    }

 

    remote_integer ret(n * n);

 

    return ret;

}

 

int main() {

    remote_integer x = frumple(5);

 

    cout << x.get() << endl;

 

    remote_integer y = frumple(1729);

 

    cout << y.get() << endl;

}

 

C:Temp>cl /EHsc /nologo /W4 /O2 unified_wrong.cpp

unified_wrong.cpp

 

C:Temp>unified_wrong

Unary constructor.

Copy constructor.

Copy assignment operator.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

C:Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_wrong.cpp

unified_wrong.cpp

 

C:Temp>unified_wrong

Unary constructor.

MOVE CONSTRUCTOR.

Copy assignment operator.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

(编译器在这里进行了返回值优化(RVO),但不是具名返回值优化(NRVO)。就像我之前提到的,有些拷贝构造函数被 RVO 或 NRVO 优化掉了,但编译器并不总是能够做这样的优化,这时剩余的就由 move 构造函数来优化。)


move 构造函数中标记为 WRONG 的那一行,调用了拷贝赋值函数,编译能通过也能运行,但这违背了 move 构造函数的本意。(译注:因为那个拷贝赋值函数只是进行普通的拷贝赋值,而不是 move 赋值!)


这是怎么回事呢?记住:在C++98/03中,具名 lvalue 引用是左值(给定语句 int& r = *p; r 是 lvalue),不具名 lvalue 引用还是左值(给定语句 vector<int> v(10, 1729), v[0] 返回 int&, 你可以对这个不具名 lvalue 引用取址)。但是 rvalue 引用就不一样了:


· 具名 lvalue 引用是 lvalue

· 不具名 rvalue 引用是 rvalue


一个具名 rvalue 引用是一个 lvalue 是因为可以对它施加多重操作,重复使用。相反,如果它是一个 ravlue 的话,那么对它施加的第一个操作能够“窃取”它,而后续操作就没机会了。这里的“窃取”是说不会被察觉到,所以这是行不通的。另一方面,不具名 rvalue 引用不能被重复使用,所以它仍保持右值(rvalueness)语意。


如果你真的打算用 move 赋值函数来实现 move 构造函数,你需要从 lvalue move,就像是从 rvalue move 一样。C++0x <utility> 中的 std::move() 具备这样的能力,VC10将会有这个(实际上,开发版中已经有了),但VC10 TCP版还没有,所以我会教你从头做起:


C:Temp>type unified_right.cpp

#include <stddef.h>

#include <iostream>

#include <ostream>

using namespace std;

 

template <typename T> struct RemoveReference {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&> {

     typedef T type;

};

 

template <typename T> struct RemoveReference<T&&> {

     typedef T type;

};

 

template <typename T> typename RemoveReference<T>::type&& Move(T&& t) {

    return t;

}

 

class remote_integer {

public:

    remote_integer() {

        cout << "Default constructor." << endl;

 

        m_p = NULL;

    }

 

    explicit remote_integer(const int n) {

        cout << "Unary constructor." << endl;

 

        m_p = new int(n);

    }

 

    remote_integer(const remote_integer& other) {

        cout << "Copy constructor." << endl;

 

        m_p = NULL;

        *this = other;

    }

 

#ifdef MOVABLE

    remote_integer(remote_integer&& other) {

        cout << "MOVE CONSTRUCTOR." << endl;

 

        m_p = NULL;

        *this = Move(other); // RIGHT

    }

#endif // #ifdef MOVABLE

 

    remote_integer& operator=(const remote_integer& other) {

        cout << "Copy assignment operator." << endl;

 

        if (this != &other) {

            delete m_p;

 

            if (other.m_p) {

                m_p = new int(*other.m_p);

            } else {

                m_p = NULL;

            }

        }

 

        return *this;

    }

 

#ifdef MOVABLE

    remote_integer& operator=(remote_integer&& other) {

        cout << "MOVE ASSIGNMENT OPERATOR." << endl;

 

        if (this != &other) {

            delete m_p;

 

            m_p = other.m_p;

            other.m_p = NULL;

        }

 

        return *this;

    }

#endif // #ifdef MOVABLE

 

    ~remote_integer() {

        cout << "Destructor." << endl;

 

        delete m_p;

    }

 

    int get() const {

        return m_p ? *m_p : 0;

    }

 

private:

    int * m_p;

};

 

remote_integer frumple(const int n) {

    if (n == 1729) {

        return remote_integer(1729);

    }

 

    remote_integer ret(n * n);

 

    return ret;

}

 

int main() {

    remote_integer x = frumple(5);

 

    cout << x.get() << endl;

 

    remote_integer y = frumple(1729);

 

    cout << y.get() << endl;

}

 

C:Temp>cl /EHsc /nologo /W4 /O2 /DMOVABLE unified_right.cpp

unified_right.cpp

 

C:Temp>unified_right

Unary constructor.

MOVE CONSTRUCTOR.

MOVE ASSIGNMENT OPERATOR.

Destructor.

25

Unary constructor.

1729

Destructor.

Destructor.

 

(我将交替提及 std::move() 和我自己的 Move(),因为它们的实现是等价的) std::move() 是怎样工作的呢?目前,我只能跟你说这是“魔法”。(后面会有完整的解释,并不复杂,但它与模板参数推导和引用折叠(reference collapsing,译注:引用的引用)有关,后面讲完美转发的时候我们还会遇到这两个东西)。我可以用一个具体的例子来略过“魔法”:给定一个 string 类型的左值,像前面重载决议例子中的 up ,std::move(up) 调用 string&& std::move(string&),这个函数返回一个不具名的 rvalue 引用,它是一个 rvalue。给定一个 string 类型的 rvalue,像前面重载决议例子中的 strange(), std::move(strange()) 调用 string&& std::move(string&&),同样这个函数还是返回一个不具名的 rvalue,还是 rvalue。


std::move() 除了让你能用 move 复制函数来实现 move 构造函数之外,还能在其他地方发挥作用。无论何时,只要你有一个左值,而它的值也不再重要了(例如,它将被销毁或被赋值),你就可以使用 std::move(你的左值表达式) 来使用 move 语意。

 

 <  第一页  第二页  本页 第四页 第五页>

 

 

 

分享:

标签:C++0x,

添加评论