如何过滤掉std::variant向量中某些数据类型的元素?
How to filter out elements of certain data types in a vector of std::variant?
我有 std::vector
个 std::variant
个类型为 int
或 std::set<int>
的元素。如果迭代元素的类型为 std::set<int>
,我想遍历此向量和 insert
一个额外的项目。但是,似乎不允许在 运行 时间查询索引。我怎样才能做到这一点?
#include <variant>
#include <set>
#include <vector>
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for (int i = 0; i < var_vec.size(); ++i)
{
// if the element var_vec[i] is of type std::set<int>
if (var_vec[i].index() == 1) var_vec[i].insert(888); // !ERROR! How to achieve this?
}
return 0;
}
错误信息:
error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'
在运行时调用 index()
没有错,这种情况一直都在发生。真正的问题是:
var_vec[i].insert(888);
var_vec[i]
是一个 std::variant
。它没有名为 insert()
.
的方法
std::get<1>(var_vec[i]).insert(888);
这为您提供了变体集,它很乐意让您insert()
做一些事情。
这种整体方法的一个问题是,如果您出于某种原因想要修改您的变体,并且 std::set
不再是其第二个替代方案,出于某种原因,上述所有逻辑都会又断了。
你应该考虑using std::holds_alternative
, instead of index()
, and using a type with std::get
而不是索引,它会自动适应这种变化。
I want to loop over this vector and insert an extra item if the iterated element is of type std::set. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
您不需要在运行时检查类型,您可以只使用std::visit()
并在编译时检查变体的类型是否为std::set<int>
-时间:
// Others headers go here...
#include <type_traits>
// ...
for (int i = 0; i < var_vec.size(); ++i)
// if the element var_vec[i] is of type std::set<int>
std::visit([](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::set<int>>)
arg.insert(888);
}, var_vec[i]);
正如已经指出的那样,您在 std::variant
而不是 std::set
上调用 insert
。
我使用的一种方法是使用 std::get_if
,如下所示:
#include <variant>
#include <set>
#include <vector>
int main( ) {
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for ( auto& v : var_vec ) {
// If the variant does not hold a set, get_if returns a nullptr.
if ( auto set{ std::get_if<std::set<int>>( &v ) } ) {
set->insert( 888 );
}
}
// Or you could use holds_alternative with get, but this isn't as clean.
for ( auto& v : var_vec ) {
if ( std::holds_alternative<std::set<int>>( v ) ) {
auto set{ std::get<std::set<int>>( v ) };
set.insert( 999 );
}
}
}
这允许您测试类型并使用包含的替代项。
过滤可以通过ranges::views::filter
来完成,我们可以用std::visit
来组成谓词。
为此,我们需要为 std::visit
提供关于类型的谓词。类型上的谓词可以由一组重叠的函数组成,每个函数都接受一个类型,并且所有 return a bool
;您可以通过 boost::hana::overload
.
创建它
但是,要过滤变体,我们只需要向 std::visit
提供第一个参数,并保留其第二个参数;一种方法是将 std::visit
包裹在嵌套的 lambda 中,如下所示:
auto visit_with = [](auto const& fs){
return [&fs](auto const& xs) {
return std::visit(fs, xs);
};
};
另一种方法是使用 BOOST_HOF_LIFT
和 boost::hana::curry
结果使其部分适用,这全部在一行中完成:
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
完整代码如下:
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <set>
#include <variant>
#include <vector>
using namespace boost::hana;
using namespace ranges::views;
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
// predicate
auto constexpr true_for_sets = overload(
[](std::set<int> const&){ return true; },
[](auto const&){ return false; });
// function object wrapping std::visit
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
// filter
auto var_vec_out = var_vec | filter(visit_with(true_for_sets));
}
这就是您在标题中提到的过滤。但是,在问题的 body 中,您实际上并不是在进行过滤,而是在进行其他操作,因为您在遍历集合时正在对其进行修改。
我有 std::vector
个 std::variant
个类型为 int
或 std::set<int>
的元素。如果迭代元素的类型为 std::set<int>
,我想遍历此向量和 insert
一个额外的项目。但是,似乎不允许在 运行 时间查询索引。我怎样才能做到这一点?
#include <variant>
#include <set>
#include <vector>
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for (int i = 0; i < var_vec.size(); ++i)
{
// if the element var_vec[i] is of type std::set<int>
if (var_vec[i].index() == 1) var_vec[i].insert(888); // !ERROR! How to achieve this?
}
return 0;
}
错误信息:
error: '__gnu_cxx::__alloc_traits<std::allocator<std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >, std::variant<int, std::set<int, std::less<int>, std::allocator<int> > > >::value_type' {aka 'class std::variant<int, std::set<int, std::less<int>, std::allocator<int> > >'} has no member named 'insert'
在运行时调用 index()
没有错,这种情况一直都在发生。真正的问题是:
var_vec[i].insert(888);
var_vec[i]
是一个 std::variant
。它没有名为 insert()
.
std::get<1>(var_vec[i]).insert(888);
这为您提供了变体集,它很乐意让您insert()
做一些事情。
这种整体方法的一个问题是,如果您出于某种原因想要修改您的变体,并且 std::set
不再是其第二个替代方案,出于某种原因,上述所有逻辑都会又断了。
你应该考虑using std::holds_alternative
, instead of index()
, and using a type with std::get
而不是索引,它会自动适应这种变化。
I want to loop over this vector and insert an extra item if the iterated element is of type std::set. However, it seems that querying the index at run-time is not allowed. How can I achieve this?
您不需要在运行时检查类型,您可以只使用std::visit()
并在编译时检查变体的类型是否为std::set<int>
-时间:
// Others headers go here...
#include <type_traits>
// ...
for (int i = 0; i < var_vec.size(); ++i)
// if the element var_vec[i] is of type std::set<int>
std::visit([](auto&& arg) {
if constexpr (std::is_same_v<std::decay_t<decltype(arg)>, std::set<int>>)
arg.insert(888);
}, var_vec[i]);
正如已经指出的那样,您在 std::variant
而不是 std::set
上调用 insert
。
我使用的一种方法是使用 std::get_if
,如下所示:
#include <variant>
#include <set>
#include <vector>
int main( ) {
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
for ( auto& v : var_vec ) {
// If the variant does not hold a set, get_if returns a nullptr.
if ( auto set{ std::get_if<std::set<int>>( &v ) } ) {
set->insert( 888 );
}
}
// Or you could use holds_alternative with get, but this isn't as clean.
for ( auto& v : var_vec ) {
if ( std::holds_alternative<std::set<int>>( v ) ) {
auto set{ std::get<std::set<int>>( v ) };
set.insert( 999 );
}
}
}
这允许您测试类型并使用包含的替代项。
过滤可以通过ranges::views::filter
来完成,我们可以用std::visit
来组成谓词。
为此,我们需要为 std::visit
提供关于类型的谓词。类型上的谓词可以由一组重叠的函数组成,每个函数都接受一个类型,并且所有 return a bool
;您可以通过 boost::hana::overload
.
但是,要过滤变体,我们只需要向 std::visit
提供第一个参数,并保留其第二个参数;一种方法是将 std::visit
包裹在嵌套的 lambda 中,如下所示:
auto visit_with = [](auto const& fs){
return [&fs](auto const& xs) {
return std::visit(fs, xs);
};
};
另一种方法是使用 BOOST_HOF_LIFT
和 boost::hana::curry
结果使其部分适用,这全部在一行中完成:
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
完整代码如下:
#include <boost/hana/functional/overload.hpp>
#include <boost/hana/functional/partial.hpp>
#include <iostream>
#include <range/v3/view/filter.hpp>
#include <set>
#include <variant>
#include <vector>
using namespace boost::hana;
using namespace ranges::views;
int main()
{
using Variants = std::variant<int, std::set<int>>;
std::vector<Variants> var_vec;
var_vec.push_back(999);
std::set<int> a = {0,1,2};
var_vec.push_back(a);
// predicate
auto constexpr true_for_sets = overload(
[](std::set<int> const&){ return true; },
[](auto const&){ return false; });
// function object wrapping std::visit
auto visit_with = curry<2>(BOOST_HOF_LIFT(std::visit));
// filter
auto var_vec_out = var_vec | filter(visit_with(true_for_sets));
}
这就是您在标题中提到的过滤。但是,在问题的 body 中,您实际上并不是在进行过滤,而是在进行其他操作,因为您在遍历集合时正在对其进行修改。