允许类的用户移动私有成员

Emi*_*ier 13 c++ encapsulation move c++11

说我有这个班:

class Message
{
public:
    using Payload = std::map<std::string, boost::any>;

    Message(int id, Payload payload)
    : id_(id),
      payload_(std::move(payload))
    {}

    int id() const {return id_;}

    const Payload& payload() const {return payload_;}

private:
    int id_;
    Payload payload_;
};
Run Code Online (Sandbox Code Playgroud)

Payload复制的地方可能很大而且价格昂贵.

我想给这个Message类的用户提供移动有效负载的机会,而不是必须复制它.这样做最好的方法是什么?

我可以想到以下几种方式:

  1. 添加一个Payload& payload()返回可变引用的重载.用户可以这样做:

    Payload mine = std::move(message.payload())

  2. 不要假装我正在封装payload_,只是让它成为公共成员.

  3. 提供takePayload会员功能:

    Payload takePayload() {return std::move(payload_);}

  4. 提供此备用成员功能:

    void move(Payload& dest) {dest = std::move(payload_);}

  5. (由Tavian Barnes提供)提供payload使用ref-qualifier的getter重载:

    const Payload& payload() const {return payload_;}

    Payload payload() && {return std::move(payload_);}

替代#3似乎是在std :: future :: get,overload(1)中完成的.

关于什么是最佳替代方案(或另一种解决方案)的任何建议将不胜感激.


编辑:这是我正在努力完成的一些背景知识.在我的实际工作中,这个Message类是一些通信中间件的一部分,并包含一堆用户可能或可能不感兴趣的其他元数据.我原以为用户可能想要将有效负载数据移动到他的或者她自己的数据结构,并在收到后丢弃原始的Message对象.

Die*_*ühl 3

看来最一致的方法是使用选项 5:

  1. 似乎不建议使用第一个和第二个选项,因为它们公开了实现细节。
  2. 当使用Message右值时,例如从函数返回,应该直接移动有效负载,并且它使用第五个选项:

    Messsage some_function();
    Payload playload(some_function().payload()); // moves
    
    Run Code Online (Sandbox Code Playgroud)
  3. 与表达式一起使用std::move(x)通常表示 的值x不依赖于前进,并且其内容可能已被转移。第 5 个选项与该表示法一致。

  4. 使用相同的名称并让编译器确定内容是否可以移动,可以使通用上下文中的操作变得更容易:

    template <typename X>
    void f(X&& message_source) {
        Payload payload(message_source.get_message());
    }
    
    Run Code Online (Sandbox Code Playgroud)

    根据是否get_message()产生左值或右值,有效负载被适当地复制或移动。第三种选择不会产生这种好处。

  5. 返回值使得可以在复制省略避免进一步潜在复制或移动的上下文中使用获得的值:

    return std::move(message).payload(); // copy-elision enabled
    
    Run Code Online (Sandbox Code Playgroud)

    这是第四个选项无法产生的结果。

在资产负债表的负面方面,很容易错误地尝试移动有效负载:

return std::move(message.payload()); // whoops - this copies!
Run Code Online (Sandbox Code Playgroud)

请注意,第五个选项的其他重载需要以不同的方式声明:

Payload        payload() &&     { return std::move(this->payload_); }
Payload const& payload() const& { return this->payload_; }
          // this is needed --^
Run Code Online (Sandbox Code Playgroud)