如何移动对象的数据成员?

时间:2016-11-26 11:14:58

标签: c++11 move-semantics

想象一下,我们将一个对象传递给一个函数,并希望将语义移动到其各个成员中。选择哪个选项:在每个成员上调用std::move或移动整个对象(多次),然后将其成员作为rvalues访问?

Foo::Foo(Object object)
// this way?
: member1(std::move(object.member1)), member2(std::move(object.member2)) {} 
// or this way?
: member2(std::move(object).member2), member2(std::move(object).member2) {}

更具体地说,请考虑以下示例。有一个下载器类,它接受连接参数作为对象。我想移动 - 将其成员分配给下载程序的成员,所以我正在执行以下操作:

class ConnectionParameters
{
public:
    ConnectionParameters(string server, string login, string password, unsigned short port, unsigned int connectionLimit, unsigned long long speedLimit)
        : server(move(server))
        , login(move(login))
        , password(move(password))
        , port(port)
        , connectionLimit(connectionLimit)
        , speedLimit(speedLimit) {}

    ConnectionParameters(const ConnectionParameters &) = delete;    // disable copy constructor
    ConnectionParameters(ConnectionParameters &&) = default;    // default move constructor

    const string & getServer() const & { return this->server; }
    string && getServer() && { return move(this->server); }

    const string & getLogin() const & { return this->login; }
    string && getLogin() && { return move(this->login); }

    const string & getPassword() const & { return this->password; }
    string && getPassword() && { return move(this->password); }

    unsigned short getPort() const  { return this->port; }
    unsigned int getConnectionLimit() const { return this->connectionLimit; }
    unsigned long long getSpeedLimit() const { return this->speedLimit; }

private:
    string server, login, password;
    unsigned short port;
    unsigned int connectionLimit;
    unsigned long long speedLimit;
};

class Downloader
{
public:
    Downloader(ConnectionParameters connectionParameters, string downloadDirectory, unsigned long long diskSpaceMinimum)
        : server(move(connectionParameters).getServer()) // 1st move
        , login(move(connectionParameters).getLogin()) // 2nd move!
        , password(move(connectionParameters).getPassword()) // 3rd move!
        , port(connectionParameters.getPort())
        , connectionLimit(connectionParameters.getConnectionLimit())
        , speedLimit(connectionParameters.getSpeedLimit())
        , downloadDirectory(move(downloadDirectory))
        , diskSpaceMinimum(diskSpaceMinimum) {}

private:
    string server, login, password;
    unsigned short port;
    unsigned int connectionLimit;
    unsigned long long speedLimit;
    string downloadDirectory;
    unsigned long long diskSpaceMinimum;
};

我不喜欢这种方法是在同一个std::move对象上多次调用connectionParameters。虽然它有效但它可能违反了移动语义的原则(移动后不能使用该对象)。

另一种方法是单独移动成员:

struct ConnectionParameters
{
    string server, login, password;
    unsigned short port;
    unsigned int connectionLimit;
    unsigned long long speedLimit;

    ConnectionParameters(string server, string login, string password, unsigned short port, unsigned int connectionLimit, unsigned long long speedLimit)
        : server(move(server))
        , login(move(login))
        , password(move(password))
        , port(port)
        , connectionLimit(connectionLimit)
        , speedLimit(speedLimit) {}

    ConnectionParameters(const ConnectionParameters &) = delete;    // disable copy constructor
    ConnectionParameters(ConnectionParameters &&) = default;    // default move constructor
};

class Downloader
{
public:
    Downloader(ConnectionParameters connectionParameters, string downloadDirectory, unsigned long long diskSpaceMinimum)
        : server(move(connectionParameters.server))
        , login(move(connectionParameters.login))
        , password(move(connectionParameters.password))
        , port(connectionParameters.port)
        , connectionLimit(connectionParameters.connectionLimit)
        , speedLimit(connectionParameters.speedLimit)
        , downloadDirectory(move(downloadDirectory))
        , diskSpaceMinimum(diskSpaceMinimum) {}

private:
    string server, login, password;
    unsigned short port;
    unsigned int connectionLimit;
    unsigned long long speedLimit;
    string downloadDirectory;
    unsigned long long diskSpaceMinimum;
};

我不喜欢的是ConnectionParameters结构的成员直接暴露(否则,Downloader将无法移动它们。)

这样做的正确方法是什么?

0 个答案:

没有答案