您能否根据是否使用 return 值来保证不同的生命周期行为?
Can you guarantee different lifetime behavior based on whether a return value is used?
假设我们有某种 subscriber
对象在销毁时利用 RAII 清理自身。
我们有记录在案的代码声明以下内容:
1. If you capture the return type, the subscriber will live as long as the value you captured lives.
2. If you don't capture the return type, the subscriber will live as long as the object whose method you called to create the subscriber.
澄清一下,代码如下所示:
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.
我们能否保证根据 return 类型是否被捕获来转移所有权?
我不确定您打算如何完成 #2,但您可以通过 return 将代理对象转换(最好从右值引用)到调用者应存储的类型("capture" 表示其他含义)。
该转换可以执行任意操作,例如从自动生命周期列表中删除对象。
允许编译器在 return 序列(例如 RVO、NRVO)期间省略复制构造函数调用。但它不能省略转换。
我对您使用的术语有点费解,但如果我答对了问题,我想,它的意思是以下受您的代码示例启发的快速而肮脏的 mce。
如果不为connect_subscriber的return值提供存储,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
假设我们有某种 subscriber
对象在销毁时利用 RAII 清理自身。
我们有记录在案的代码声明以下内容:
1. If you capture the return type, the subscriber will live as long as the value you captured lives.
2. If you don't capture the return type, the subscriber will live as long as the object whose method you called to create the subscriber.
澄清一下,代码如下所示:
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.
我们能否保证根据 return 类型是否被捕获来转移所有权?
我不确定您打算如何完成 #2,但您可以通过 return 将代理对象转换(最好从右值引用)到调用者应存储的类型("capture" 表示其他含义)。
该转换可以执行任意操作,例如从自动生命周期列表中删除对象。
允许编译器在 return 序列(例如 RVO、NRVO)期间省略复制构造函数调用。但它不能省略转换。
我对您使用的术语有点费解,但如果我答对了问题,我想,它的意思是以下受您的代码示例启发的快速而肮脏的 mce。
如果不为connect_subscriber的return值提供存储,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