如何使用 range-v3 获取集合的所有权?
How can I take ownership of a collection using range-v3?
我想 return 表示 STL 集合视图的函数的范围,如下所示:
auto createRange() {
std::unordered_set<int> is = {1, 2, 3, 4, 5, 6};
return is | view::transform([](auto&& i) {
return i;
});
}
然而,view::transform
没有取得 is
的所有权,所以当我 运行 这个时,有未定义的行为,因为 is
在 [=18] 时被释放=] 退出。
int main(int argc, char* argv[]) {
auto rng = createRange();
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
如果我尝试将 std::move(is)
作为输入,我会得到一个静态断言,表明我不能使用右值引用作为 view
的输入。有什么方法可以确保视图拥有集合的所有权?
编辑:一些附加信息
我想添加一些澄清信息。我有一个数据流,data
,我有一个视图将数据转换为一个结构,Foo
,看起来像这样:
struct Foo {
std::string name;
std::unordered_set<int> values;
}
// Take the input stream and turn it into a range of Foos
auto foos = data | asFoo();
我想要做的是通过在整个值中分配名称来创建一个 std::pair<std::string, int>
的范围。我天真的尝试看起来像这样:
auto result = data | asFoo() | view::transform([](auto&& foo) {
const auto& name = foo.name;
const auto& values = foo.values;
return values | view::transform([name](auto&& value) {
return std::make_pair(name, value);
}
}) | view::join;
但是,这会导致未定义的行为,因为 values
已被释放。我能够解决这个问题的唯一方法是使 values
成为 std::shared_ptr
并在传递给 view::transform
的 lambda 中捕获它以保持它的生命周期。这似乎是一个不优雅的解决方案。
我想我正在寻找的是一个将拥有源集合所有权的视图,但它看起来不像 range-v3 那样。
或者,我可以使用老式的 for 循环创建分布式版本,但这似乎不适用于 view::join
:
auto result = data | asFoo() | view::transform([](auto&& foo) {
const auto& name = foo.name;
const auto& values = foo.values;
std::vector<std::pair<std::string, std::string>> distributedValues;
for (const auto& value : values) {
distributedValues.emplace_back(name, value);
}
return distributedValues;
}) | view::join;
即使这确实适用于 view::join
,我也认为范围和循环的混合隐喻也不优雅。
视图不拥有它们呈现的数据。如果需要保证数据的持久化,那么数据本身也需要被保存。
auto createRange() {
//We're using a pointer to ensure that the contents don't get moved around, which might invalidate the view
std::unique_ptr<std::unordered_set<int>> is_ptr = std::make_unique<std::unordered_set<int>>({1,2,3,4,5,6});
auto & is = *is_ptr;
auto view = is | view::transform([](auto&& i) {return i;});
return std::make_pair(view, std::move(is_ptr));
}
int main() {
auto[rng, data_ptr] = createRange();
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
另一种方法是确保向函数提供创建视图的数据集:
auto createRange(std::unordered_set<int> & is) {
return is | view::transform([](auto&& i) {return i;});
}
int main() {
std::unordered_set<int> is = {1,2,3,4,5,6};
auto rng = createRange(is);
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
任何一种解决方案都应广泛代表您的项目解决方案需要做什么。
我想 return 表示 STL 集合视图的函数的范围,如下所示:
auto createRange() {
std::unordered_set<int> is = {1, 2, 3, 4, 5, 6};
return is | view::transform([](auto&& i) {
return i;
});
}
然而,view::transform
没有取得 is
的所有权,所以当我 运行 这个时,有未定义的行为,因为 is
在 [=18] 时被释放=] 退出。
int main(int argc, char* argv[]) {
auto rng = createRange();
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
如果我尝试将 std::move(is)
作为输入,我会得到一个静态断言,表明我不能使用右值引用作为 view
的输入。有什么方法可以确保视图拥有集合的所有权?
编辑:一些附加信息
我想添加一些澄清信息。我有一个数据流,data
,我有一个视图将数据转换为一个结构,Foo
,看起来像这样:
struct Foo {
std::string name;
std::unordered_set<int> values;
}
// Take the input stream and turn it into a range of Foos
auto foos = data | asFoo();
我想要做的是通过在整个值中分配名称来创建一个 std::pair<std::string, int>
的范围。我天真的尝试看起来像这样:
auto result = data | asFoo() | view::transform([](auto&& foo) {
const auto& name = foo.name;
const auto& values = foo.values;
return values | view::transform([name](auto&& value) {
return std::make_pair(name, value);
}
}) | view::join;
但是,这会导致未定义的行为,因为 values
已被释放。我能够解决这个问题的唯一方法是使 values
成为 std::shared_ptr
并在传递给 view::transform
的 lambda 中捕获它以保持它的生命周期。这似乎是一个不优雅的解决方案。
我想我正在寻找的是一个将拥有源集合所有权的视图,但它看起来不像 range-v3 那样。
或者,我可以使用老式的 for 循环创建分布式版本,但这似乎不适用于 view::join
:
auto result = data | asFoo() | view::transform([](auto&& foo) {
const auto& name = foo.name;
const auto& values = foo.values;
std::vector<std::pair<std::string, std::string>> distributedValues;
for (const auto& value : values) {
distributedValues.emplace_back(name, value);
}
return distributedValues;
}) | view::join;
即使这确实适用于 view::join
,我也认为范围和循环的混合隐喻也不优雅。
视图不拥有它们呈现的数据。如果需要保证数据的持久化,那么数据本身也需要被保存。
auto createRange() {
//We're using a pointer to ensure that the contents don't get moved around, which might invalidate the view
std::unique_ptr<std::unordered_set<int>> is_ptr = std::make_unique<std::unordered_set<int>>({1,2,3,4,5,6});
auto & is = *is_ptr;
auto view = is | view::transform([](auto&& i) {return i;});
return std::make_pair(view, std::move(is_ptr));
}
int main() {
auto[rng, data_ptr] = createRange();
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
另一种方法是确保向函数提供创建视图的数据集:
auto createRange(std::unordered_set<int> & is) {
return is | view::transform([](auto&& i) {return i;});
}
int main() {
std::unordered_set<int> is = {1,2,3,4,5,6};
auto rng = createRange(is);
ranges::for_each(rng, [](auto&& i) {
std::cout << std::to_string(i) << std::endl;
});
}
任何一种解决方案都应广泛代表您的项目解决方案需要做什么。