您能否根据是否使用返回值来保证不同的生命周期行为?

时间:2017-08-22 22:29:29

标签: c++ move rvalue ownership

假设我们有某种subscriber对象利用RAII在破坏时清理自己。

我们有记录的代码声明如下:

  

1。如果您捕获了返回类型,只要您捕获的值存在,订阅者就会生效。

     

2. 如果您没有捕获返回类型,则只要您调用其方法创建订阅者的对象,订阅者就会生存。

为了澄清,代码如下所示:

template <class... Args>
id &&connect_subscriber(Args &&... args)
{
    auto id = connect_subscriber_with_id(std::forward<Args>(args)...);

    {
        std::lock_guard<std::mutex> lock(subscriber_id_mutex_);
        subscriber_ids_.push_back(std::move(id));
        return std::move(subscriber_ids_.back());
    }
}

文档是:

/// If the resulting `subscriber_id` is assigned storage at the call
/// site, then the subscription will be managed exclusively by the caller.
/// This means that until the identifier is either destroyed or
/// `disconnect_subscriber()` is called with it, the subscription will stay
/// active.
///
/// Conversely, if the `subscriber_id` is not assigned storage at
/// the call site, it will be managed internally inside the task and the
/// subscription will persist for the life-time of the task.

我们能否保证根据是否捕获了返回类型来转移所有权?

2 个答案:

答案 0 :(得分:1)

我对你使用的术语有点挣扎,但是如果我得到了正确的问题,我认为,这意味着你的代码示例启发了以下快速而肮脏的mce。

如果你没有为connect_subscriber的返回值提供存储,std :: move将不会成功,id将保留在subscriber_ids_中,否则它将被移动到你的存储中,从而从subscriber_ids_中删除,而不是由任何管理机制适用于subscriber_ids _。

#include <vector>
#include <iostream>

using id = std::string;
std::vector<std::string> subscriber_ids_ = {"31"};

int ids = 42;

id&& connect_subscriber()
{
    auto id = std::to_string(ids++);
    {
        subscriber_ids_.push_back(std::move(id));
        return std::move(subscriber_ids_.back());
    }
}

void print() {
    int i=0;
    for(const auto& n:subscriber_ids_)
        std::cout << i++ << ":" << n << " ";
    std::cout << "\n";
}

int main()
{

    connect_subscriber();    // memory not privided
    print();

    connect_subscriber();    // memory not privided
    print();

    auto take_it = connect_subscriber();    // memory privided
    print();
}

输出是:

0:31 1:42 
0:31 1:42 2:43 
0:31 1:42 2:43 3:  

输出

0:31 1:42 
0:31 1:42 2:43 
0:31 1:42 2:43 3:44 

答案 1 :(得分:0)

我不确定你的目标是如何完成#2,但你可以通过返回一个带有转换的代理对象(最好是从rvalue引用)到调用者应该存储的类型(“capture”意味着别的东西)来区分。

该转换可以执行任意操作,例如从自动生命周期列表中删除对象。

允许编译器在返回序列(例如RVO,NRVO)期间忽略复制构造函数调用。但它无法避免转换。