从标准容器生成std::元组
Generate a std::tuple from standard container
是否有一种可移植的方法来从容器的内容生成std::tuple
(实际上是std::array
) ?这样的元组将允许std::apply
从容器中提取函数参数。
我的第一次尝试,使用尾部递归,失败的编译器错误:"递归模板实例化超过最大值…"。
我不能完全得到我的第二次尝试(std::for_each
与可变lambda持有元组),以编译所需的结果。
我假设boost::mpl
处理可变元函数的方式(即使用boost::preprocessor
的魔法)可以工作-但这是c++03。我希望有更好的解决办法。
函数签名看起来像:
std::list<int> args_as_list = {1, 2, 3, 4};
auto tpl = args_as_tuple(args_as_list);
其中tpl
的类型为std::array<int const, 4>
简短的回答:no,这是不可能的
说明:std::tuple
和std::array
都需要编译时关于元素个数的信息。std::list
或std::vector
只能提供关于元素计数的运行时信息。
你的args_as_tuple
函数必须是一个模板,将预期参数的数量作为模板参数(args_as_tuple<4>(args_as_list)
)。
虽然必须将参数数量作为模板参数看起来很苛刻,但在您的示例中,这是相当明显的-函数参数数量(提供给std::apply
的函数)也必须在编译时知道。
对于更泛型的代码,你可以使用:function-traits或者来自这个答案的代码。
或者使用std::array
而不是std::list
(大量通用模板代码,但良好的编译时检查)
std::tuple
或std::array
中的元素数量是其类型信息的一部分。因此,上面建议的函数args_as_tuple
将不得不以某种方式成为模板,并且结果的每种不同可能大小将需要该模板的不同实例化。因此,你不能编写一个程序来支持任意大小的元组,除非该程序的代码是无限的(这是不可能的)。
如果你只关心int
的取值范围,比如说,你可以实例化模板40亿次,但是你的可执行文件将至少有4gb大。
如果你在实际的程序中真的只关心几个不同大小的向量,你可以只实例化这些模板,然后编写转换代码,对std::list::size()
的值进行大小写计算,并调用相应的函数(繁琐)。
但是你确切的代码片段
std::list<int> args_as_list = {1, 2, 3, 4};
auto tpl = args_as_tuple(args_as_list);
在c++中不能工作。因为,在c++中,所有变量必须在编译时确定一个已知的类型。即使您使用关键字auto
, auto
也必须在编译时解析为固定类型,这意味着如果它是元组或数组,则固定大小,无论表达式args_as_tuple
正在执行何种模板操作。
由于我的问题无法解决,我解决了一个稍微不同的问题,使我能够继续前进。
我想出了一个解决方案,它允许我从容器中提取函子的参数。我可以用我想求值的函子实例化一个eval_container
,然后将容器传递给结果对象。
#include <utility>
template <int N>
using Int = std::integral_constant<int, N>;
template <typename T>
struct arity : arity<decltype(&T::operator())> {};
template <typename T, typename RT, typename...Args>
struct arity<RT(T::*)(Args...) const>
{
// could enforce maximum number of arguments
static constexpr int value = sizeof...(Args);
};
template <typename F, int N = arity<F>::value>
struct eval_container
{
eval_container(F const& f) : f(f) {}
eval_container(F&& f) : f(std::move(f)) {}
template <typename Iter, typename I, typename...Args>
auto operator()(Iter&& iter, I, Args&&...args) const
{
// assert(iter != end)
auto&& arg = *iter++;
return (*this)(std::forward<Iter>(iter)
, Int<I()-1>{}
, std::forward<Args>(args)...
, arg);
}
template <typename Iter, typename...Args>
auto operator()(Iter&&, Int<0>, Args&&...args) const
{
// assert(iter == end)
return f(std::forward<Args>(args)...);
}
template <typename C>
auto operator()(C const& container) const
{
return (*this)(container.begin(), Int<N>{});
}
F f;
};
}
- 专用于 std 元组的模板,而无需用户执行remove_cvref
- std::元组分配和复制/移动异常保证
- std::元组和移动语义
- std::元组作为成员替换,方便宏
- 如何从参数包构造引用的 std::元组?
- 用一个额外的元素扩展 std::array 的每个 std::元组
- std::元组大小,是不是错过了优化?
- 初始化语句中临时 std::元组的开销
- 如何改善STD ::元组在空置领域
- 就地解压缩 std::元组,而无需为 std::index_sequence 提供人工层
- 动态创建和扩展STD ::元组为参数包
- 获取std ::元组元素作为std ::变体
- std::元组到元组的映射和使用 emplace
- 检查是否所有 std::元组元素都满足条件 + 设计问题
- Q:std::元组/std::数组
- 为什么GCC警告使用STD ::元组和虚拟继承来调用非平凡的动作分配运算符
- 为什么使用std ::元组和模板派生的类扣除/替换会扣除/替换
- C++ 如何根据部分特化制作 std::元组类型?
- 由std ::元组存储的垃圾价值
- 使用 C++11 std::元组在大型项目中