1

考虑这段代码:

#include <iostream>
#include <string>

using namespace std;


class Movable {
public:
    Movable(const string& name) : m_name(name) { }
    Movable(const Movable& rhs) {
        cout << "Copy constructed from " << rhs.m_name << endl;
    }

    Movable(Movable&& rhs) {
        cout << "Move constructed from " << rhs.m_name << endl;
    }

    Movable& operator = (const Movable& rhs) {
        cout << "Copy assigned from " << rhs.m_name << endl;
    }

    Movable& operator = (Movable&& rhs) {
        cout << "Move assigned from " << rhs.m_name << endl;
    }

private:
    string m_name;
};


int main() {
    Movable obj1("obj1");
    Movable obj2(std::move(obj1));
    obj2 = std::move(obj1);     // For demostration only

    const Movable cObj("cObj");
    Movable tObj(std::move(cObj));
    tObj = std::move(cObj);     // For demonstration only
}

它的输出是:

Move constructed from obj1
Move assigned from obj1
Copy constructed from cObj
Copy assigned from cObj

如您所见,在这些行中,

Movable tObj(std::move(cObj));
tObj = std::move(cObj);     // For demonstration only

我打算移动cObjtObj(使用赋值运算符的第二步纯粹是为了演示)。但是,正如您在输出中看到的,cObj复制tObj.

上面的例子只是一个演示,我不知道这有什么实际用途。但我会问:

  1. 我可以移动一个const物体吗?
  2. 如果可以,这样做安全吗?

补充:我忘了问。如果我可以移动一个const物体,应该怎么做?( const_cast?)

4

2 回答 2

2

在您的代码中,复制构造函数被调用,因为移动构造函数需要一个非常量参数(Movable&&),而参数是一个 const 对象(std::move(cObj)类型为const Movable&&)。由于非 const 右值引用不能绑定 const 右值,因此该重载被丢弃,下一个最佳匹配是复制构造函数。

现在下一个问题是您是否/何时可以移出const对象。虽然从技术上讲,您可以实现一个带有 a 的移动构造函数,const &&您甚至可以在不调用未定义行为的情况下执行此操作(只是不要修改源对象的任何非可变成员),但问题是它是否有意义。而且我认为不会。

移动构造函数的存在通常表明存在由对象维护的资源,当不再需要源对象时(例如,从临时对象构造对象时),该资源可以有效地转移到另一个对象。 . 移动本质上是修改源对象。不修改源对象的移动构造函数不能将资源从源对象移动到目标对象,这几乎没有意义或根本没有意义。

于 2013-02-12T04:23:09.220 回答
2

当然,只要对象的非mutable状态不会因为被移出而改变。

也许您有一个与文件内容或数据库记录相对应的对象。并且它在访问时将该数据缓存在一个mutable成员中。现在,您可以通过窃取其缓存数据来移动该对象,而无需实际修改该对象。因此,该对象具有 move 构造函数和 move 赋值运算符是有意义的(只要对象可移动是有意义的) take const Record&&

这是完全合法的,C++11 标准的第 12.8p3 节规定:

类的非模板构造函数X是移动构造函数,如果它的第一个参数是、、 或类型X&&,并且没有其他参数或者所有其他参数都有默认参数。const X&&volatile X&&const volatile X&&

和 p19:

用户声明的移动赋值运算符X::operator=是类的非静态非模板成员函数,X只有一个类型为X&&const X&&volatile X&&或的参数const volatile X&&

这样,您可以移动一个const对象,即通过移动创建reopen的新副本,从而将缓冲区空间重用于其他内容并避免新分配,同时保持旧对象完好无损并且如果需要,准备好返回其内容(通过再次访问磁盘或数据库)。

于 2013-02-12T04:12:39.750 回答