对参数包的递归迭代

时间:2017-12-13 10:27:27

标签: c++ templates visual-studio-2015 c++14 variadic-templates

我目前正在尝试实现一个函数,该函数接受一些string str = string.Join("Insert into " + tname+ "(id, t, v) values(" + lc+ ", " + mc+ ", " + rc+ ");", Environment.NewLine); File.AppendAllText(fileName, str); 和一个参数包data。在里面我调用另一个函数,递归迭代给定的参数。

可悲的是,我有一些问题要编译它。显然编译器一直在尝试编译递归函数,但不会重载以停止递归。

有谁知道问题是什么?

...args
  

\ utility(446):错误C2338:元组索引超出范围
  ...
  \ main.cpp(56):注意:请参阅类模板的引用   实例化'std :: tuple_element< 3,std :: tuple>'正在编制

2 个答案:

答案 0 :(得分:2)

  

有谁知道问题是什么?

关键点在于:

if (Index < TotalSize)
            return ok && ParseCompositeFields<Index + 1>(data, std::forward<TArgs>(args)...);

首先,要使逻辑正确,条件应为Index < TotalSize - 1。,因为元组元素计数从零开始。 此外,即使Index == TotalSize - 1,编译器仍然被强制实例化ParseCompositeFields<Index + 1>(因为它必须编译if分支),实际上是ParseCompositeFields<TotalSize>。但这会导致您在尝试实例化std::get<TotalSize>时出现错误。

因此,为了仅在条件满足时有条件地编译if分支, 必须使用if constexpr(Index < TotalSize - 1)(参见godbolt)。对于C ++ 14,您必须使用模板特化和函数对象:

class Sample
{
    template<std::size_t Index, bool>
    struct Helper {
         template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
        static bool ParseCompositeFields(const std::vector<std::string> &data, TArgs &&...args)
        {
            auto field = std::get<Index>(std::forward_as_tuple(std::forward<TArgs>(args)...));

            //bool ok = ParseField(field, Index, data);
            auto x = data[Index];
            bool ok = true;

            return ok && Helper<Index + 1, (Index < TotalSize - 1)>::ParseCompositeFields(data, std::forward<TArgs>(args)...);
        }
    };

    template<std::size_t Index>
    struct Helper<Index, false> {
        template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
        static bool ParseCompositeFields(const std::vector<std::string> &data, TArgs &&...args) {
            volatile int a = 1 * 2 + 3;
            return true;
        }
    };

public:
    template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
    static bool ParseCompositeFieldsXXX(const std::vector<std::string> &data, TArgs &&...args)
    {
        auto field = std::get<0>(std::forward_as_tuple(std::forward<TArgs>(args)...));

        //bool ok = ParseField(field, 0, data);
        auto x = data[0];
        bool ok = true;

       return ok && Helper<1, (TotalSize > 1)>::ParseCompositeFields(data, std::forward<TArgs>(args)...);
    }

};

答案 1 :(得分:2)

替代方法

你似乎在这里使用相当古老的技术。 Simple expansion正是您要搜索的内容:

#include <cstddef>
#include <utility>
#include <tuple>
#include <vector>
#include <string>

class Sample
{
    template <std::size_t index, typename T>
    static bool parse_field(T&& field, const std::vector<std::string>& data)
    {
        return true;
    }

    template <typename Tuple, std::size_t ... sequence>
    static bool parse_impl(Tuple&& tup, const std::vector<std::string>& data, std::index_sequence<sequence...>)
    {
        using expander = bool[];
        expander expansion{parse_field<sequence>(std::get<sequence>(tup), data)...};

        bool result = true;
        for (auto iter = std::begin(expansion); iter != std::end(expansion); ++iter)
        {
            result = result && *iter;
        }

        return result;
    }

public:
    template<class ...TArgs, std::size_t TotalSize = sizeof...(TArgs)>
    static bool ParseCompositeFieldsXXX(const std::vector<std::string> &data, TArgs &&...args)
    {

        return parse_impl(std::forward_as_tuple(std::forward<TArgs>(args)...),
                          data, std::make_index_sequence<sizeof...(TArgs)>{});
    }


};

int main()
{
    short       x1 = 0;
    std::string x2;
    long long   x3 = 0;

    Sample::ParseCompositeFieldsXXX({ "1", "Sxx", "-5,32" }, x1, x2, x3);

    return 0;
}

如果您正在查看类似数组的内容,那么它就是数组。除非需要,否则不要使用递归,因为它通常会使其变得复杂。当然也有例外。

让它变得更好

如你所见,人们甚至不需要这里的课程。只需删除它。

可能存在的问题

如果调用顺序很重要,可能会出现一个问题。 IIRC,在C ++ 17之前,它没有强大的评估顺序,因此有时可能会失败。