矢量的元组和推回
Tuple of vectors and push back
我有一个向量元组,我想将初始化器列表中的每个值push_back()
放入"向量元组"中的相应向量中。代码中的create()
函数就是我想要执行此操作的地方。
template<typename...Fields>
class ComponentManager
{
using Index = int;
public:
/**
* Provides a handle to a component
**/
struct ComponentHandle {
static constexpr Index Nil = -1;
bool nil() { return index == Nil; }
const Index index;
};
ComponentHandle lookup(Entity e) {
return ComponentHandle{get(m_map,e,-1)};
}
template<int i>
auto get(ComponentHandle handle) {
return std::get<i>(m_field)[handle.index];
}
ComponentHandle create(Entity e, Fields ...fields) {
m_entity.push_back(e);
// m_fields.push_back ... ???
}
private:
std::vector<Entity> m_entity;
std::tuple<std::vector<Fields>...> m_field;
std::map<Entity,Index> m_map;
};
示例:
Entity e1, e2;
ComponentManager<int,float,int> cm{};
cm.create(e1, 42, 1337.0, 99);
cm.create(e2, 84, 2674.0, 198);
// Resulting contents of cm.m_field tuple
// {
// vector<int> [ 42, 84 ],
// vector<float> [ 1337.0, 2674.0 ]
// vector<int> [ 99, 198 ]
// }
这可能并不明显,但在C++中打开元组的方法是使用std::apply()
。使用C++17,这就像一样简单
void create(Entity e, Fields ...fields) {
m_entity.push_back(e);
std::apply([&](auto&... vs) {
(vs.push_back(fields), ...);
}, m_field);
}
对于C++14,我建议您自己实现apply()
(这是一个简短的函数),然后您需要使用扩展器技巧而不是使用fold表达式:
void create(Entity e, Fields ...fields) {
m_entity.push_back(e);
not_std::apply([&](auto&... vs) {
using swallow = int[];
(void)swallow{0,
(vs.push_back(fields), 0)...
};
}, m_field);
}
对于C++11,除了我们不能使用通用lambdas,并且实现std::apply
比链接引用中更详细(但并不更复杂)之外,大部分内容都适用。值得庆幸的是,除了缩短代码之外,我们实际上不需要做任何事情——我们知道所有的矢量类型:
void create(Entity e, Fields ...fields) {
m_entity.push_back(e);
not_std::apply([&](std::vector<Fields>&... vs) {
using swallow = int[];
(void)swallow{0,
(vs.push_back(fields), 0)...
};
}, m_field);
}
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
(void) std::initializer_list<int>{(f(std::forward<Args>(args)), 0)...};
}
ComponentHandle create(Entity e, Fields ...fields)
{
for_each_argument([&](auto field)
{
using field_type = std::vector<std::decay_t<decltype(field)>>;
std::get<field_type>(m_field).push_back(field);
},
fields...);
}
魔杖盒示例
我在问题中没有提到字段可以是相同类型的要求,因此元组中可以有几个向量。
template <typename TVec, typename TFieldTuple, std::size_t... TIdxs>
void expander(TVec& vec, TFieldTuple ft, std::index_sequence<TIdxs...>)
{
for_each_argument([&](auto idx)
{
std::get<idx>(vec).push_back(std::get<idx>(ft));
}, std::integral_constant<std::size_t, TIdxs>{}...);
}
const auto create = [](auto& vec, auto ...fields)
{
expander(vec,
std::make_tuple(fields...),
std::make_index_sequence<sizeof...(fields)>());
};
魔杖盒示例
C++14解决方案,从C++17向下实现apply
。
template<std::size_t I>
using index_t = std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};
template<class=void,std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&&f)->decltype(auto) {
return decltype(f)(f)( index<Is>... );
};
}
template<std::size_t N>
auto index_over( index_t<N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
template<class F>
auto for_each_arg( F&& f ) {
return [f = std::forward<F>(f)](auto&&...args)->decltype(auto) {
using discard=int[];
(void)discard{0,(void(
f(decltype(args)(args))
),0)...};
};
}
这些是有用的,但不是必需的:
template<class F, class Tuple>
decltype(auto) apply( F&& f, Tuple&& tuple ) {
auto count = index< std::tuple_size< std::decay_t<Tuple> >{} >;
return index_over( count )( [&](auto...Is) {
using std::get;
return std::forward<F>(f)( get<decltype(Is)::value>( std::forward<Tuple>(tuple) )... );
} );
}
template<class Tuple>
auto for_each_tuple_element(Tuple&& tuple) {
return [&](auto&& f)->decltype(auto){
return apply(
for_each_arg( decltype(f)(f) ),
std::forward<Tuple>(tuple)
);
};
}
测试代码:
int main() {
std::tuple< std::vector<int>, std::vector<char> > tup;
for_each_tuple_element( tup )( [](auto&& v) {
v.push_back(3);
});
std::cout << std::get<0>(tup).size() << "," << std::get<1>(tup).size() << "n";
}
实例
然后我们可以将其应用于您的问题。
ComponentHandle create(Entity e, Fields ...fields) {
m_entity.push_back(e);
auto indexer = index_over<sizeof...(fields>();
auto fields_tuple = std::forward_as_tuple( std::forward<Fields>(fields)... );
indexer( for_each_arg([&](auto Is){
std::get<Is>(m_fields).push_back(
std::get<Is>(decltype(fields_tuple)(fields_tuple))
);
} );
}
index_over
占用编译时间N并返回一个lambda。这个lambda接受一个可调用函数,并通过index_t<0>
到index_t<N-1>
调用它。
for_each_arg
接受一个可调用函数,并返回一个接受任意数量参数的lambda。它依次用这些参数中的每一个调用callable。
我们把Fields...fields
拼接在一起,构建一个index_over
,为它提供一组编译时的索引。然后我们将这些字段存储在r和l值引用的tuple
中。
我们在单个索引Is
上写了一个运算。然后,我们将其传递给for_each_arg
,并将返回值传递给index_over
,然后为每个索引调用处理lambda的单个索引。
有些编译器不允许非constexpr
std::integral_constant
在constexpr
上下文中转换为标量。它们是错误的和/或过时的。对于这些,您必须执行decltype(Is)::value
。
- C++:TypeDef使用元组
- Pybind11:将元组列表从Python传递到C++
- 重载元组索引运算符-C++
- 在C++中,如何通过几种类型从元组中选择多个元素
- 将fold表达式与std::一起用于两个元组
- std::ranges::elements_view,用于自定义类似元组的数据
- 将元组的向量转换/构造为堆
- 专用于 std 元组的模板,而无需用户执行remove_cvref
- 将元组的向量构造成堆
- 元组由 Swig 生成的 Python 包装器返回,用于C++向量
- 将元组类型扩展为可变参数模板?
- 时间复杂度 当具有复合数据类型(如元组或对)时?
- 类内部和外部静态 constexpr 元组之间的差异
- 可变参数模板与使用元组在参数中添加不同的数据对
- 访问和打印元组中的数据,并使用 C++14 使用模板函数显示数据
- boost::包含提升单元的元组的哈希值
- 我正在寻找一种优雅的方式来从元组向量创建tuple_element向量
- 如何在可变参数模板函数中遍历可变参数元组?
- 如何使用 SML 随机生成八进制元组
- 元组的运行时索引