从标准容器生成std :: tuple

时间:2015-08-31 14:14:15

标签: c++ c++11 boost-mpl

是否有可行的方法从容器的内容生成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>

3 个答案:

答案 0 :(得分:3)

简短回答:没有,这是不可能的。

说明:std::tuplestd::array都需要编译时有关元素数量的信息。 std::liststd::vector只能提供有关元素计数的运行时信息。

您的args_as_tuple函数必须是一个模板,将预期参数的数量作为模板参数(args_as_tuple<4>(args_as_list))。

虽然必须将参数的数量作为模板参数看起来很苛刻,但是在你的例子中它很明显 - 函数参数的数量(提供给std::apply的函数)必须在编译时知道为好。
有关更通用的代码,您可以使用:function-traits或来自this answer的代码 或者从begging而不是std::array使用std::list(很多通用模板代码,但编译时间很好)

答案 1 :(得分:1)

std::tuplestd::array中的元素数量是其类型信息的一部分。因此,上面提出的函数args_as_tuple必须以某种方式成为模板,并且每个不同的可能大小的结果将需要该模板的不同实例化。因此,除非该程序的代码是无限的(不可能),否则你不能创建一个可以支持任意多种大小的元组的程序。

如果您只关心int的值范围,比如说,您可以将模板实例化40亿次,但随后您的可执行文件将至少达到4 GB。

如果你真的只关心实际程序中几个不同大小的向量,你可以只实例化那些模板并编写转换代码,这些代码可以用std::list::size()的值来表示。调用适当的函数(繁琐)。

但您确切的代码段

std::list<int> args_as_list = {1, 2, 3, 4};
auto tpl = args_as_tuple(args_as_list);

在C ++中永远无法正常工作。因为,在C ++中,所有变量都必须在编译时确定已知类型。即使您使用关键字autoauto必须在编译时解析为固定类型,这意味着如果它是元组或数组,则为固定大小,无论何种类型模板shenanigans表达式args_as_tuple正在做。

答案 2 :(得分:1)

由于我的问题无法解决,我解决了一个稍微不同的问题,这让我继续前进。

我提出了一个解决方案,它允许我从容器中提取仿函数的参数。我可以使用我想要计算的仿函数来实例化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;
};

}