乱序模板参数规范/演绎可能吗?

时间:2017-06-07 08:33:15

标签: c++ templates

我有一个函数XElement data = new XElement("data"); data.Add( new XElement("tableName", new XAttribute("fieldname", "value"), new XAttribute("fieldname", "value2"), new XAttribute("fieldname", "value3") ) ); data.save("some.xml"); ,它将一个元组transform和一些函数tp作为参数,两个索引funcsBegin作为模板参数。函数Endtransform中的每个函数应用于元组funcs的元素,从tp到(但不包括)std::get<Begin>(tp),并将结果收集到结果元组并返回元组。这是我的代码。

std::get<End>(tp)

据我所知,只能推断出尾随模板参数。因为在这个函数中,参数template <typename ... Rs, typename ... Ts, typename ... Fn, size_t ... Idx, size_t ... Left_Idx> std::tuple<Rs ...> tranform_helper(std::index_sequence<Idx ...>, std::index_sequence<Left_Idx ...>, const std::tuple<Ts ...>& tp, Fn&& ... funcs) { return std::make_tuple<Rs ...>(std::forward<Fn>(funcs)(std::get<Idx>(tp)) ..., std::remove_reference_t<std::tuple_element_t<Left_Idx, std::tuple<Ts ...>>>(std::get<Left_Idx>(tp)) ...); }; template < typename ... Rs, typename ... Ts, typename ... Fn, size_t Begin = 0, size_t End = std::index_sequence_for<Ts ...>().size() > std::tuple<Rs ...> transform(const std::tuple<Ts ...>& tp, Fn&& ... funcs) { constexpr size_t func_app_iters = std::min(End - Begin, std::index_sequence_for<Fn ...>().size()); auto seq_idx = make_index_range<Begin, Begin + func_app_iters>(); auto left_idx = make_index_range<Begin + func_app_iters, End>(); return tranform_helper<Rs ...>(seq_idx, left_idx, tp, std::forward<Fn>(funcs) ...); }; Ts总是应该由编译器推导出来,所以它们必须放在后面。但是,参数Fn具有默认值,具体取决于参数End,必须放在Ts之后,如果我需要使用指定的Ts调用,则会出现问题transform的值。

我的问题是:有没有办法以对订单不敏感的方式进行模板参数推断?如果没有,那么是否可以使用默认值指定模板参数,同时使编译器在此之前推导出一些参数。那就是:

End

P.S。实际的实现有点不对,因为它只返回auto record = std::make_tuple(102, "alice", 2000, false); auto x = transform<int, std::string, int, bool, /*something like "Begin = 1, End = 3" */ > (record, [](const char* name) { std::string cp(name); cp[0] = static_cast<char>(toupper(cp[0])); return cp; } [](const int& age) { return (age < 0 || age > 100) ? -1 : age; } ); std::cout << record << std::endl; // (102, "Alice", -1, false) Begin之间的元素,但即便如此,模板参数的排序仍然存在问题。

1 个答案:

答案 0 :(得分:1)

是的,这是可能的(C ++ 17解决方案)

有点困难,但可能。我有一个解决方案的粗略草案 here (我想得到一些东西,因为我没有时间来解决这个问题)。

完整代码(后面的解释):

template<int Index, int Begin, int End, int Size, class... Ts>
struct transform_apply;

template<int Index, int Begin, int End, class... Ts, class Fn, class... Fns>
struct transform_apply<Index, Begin, End, Index, std::tuple<Ts...>, Fn, Fns...>{
   static std::tuple<> apply(std::tuple<Ts...>,  Fn, Fns...){return{};}
};

template<int Index, int Begin, int End, class... Ts>
struct transform_apply<Index, Begin, End, Index, std::tuple<Ts...>>{
   static std::tuple<> apply(std::tuple<Ts...>){return{};}
};

template<int Index, int Begin, int End, int Size, class... Ts, class Fn, class... Fns>
struct transform_apply<Index, Begin, End, Size, std::tuple<Ts...>, Fn, Fns...>{
   static decltype(auto) apply(std::tuple<Ts...> tup, Fn f, Fns... fs)
   {
       if constexpr (Index >= Begin && Index < End)
       {
          auto val = f(std::get<Index>(tup));
          return std::tuple_cat(std::make_tuple(val), transform_apply<Index+1, Begin, End, Size, decltype(tup), Fns...>::apply(tup, fs...));
       }
       else
       {
          auto val = std::get<Index>(tup);
          return std::tuple_cat(std::make_tuple(val), transform_apply<Index+1, Begin, End, Size, decltype(tup), Fn, Fns...>::apply(tup, f, fs...));
       }
   };
};

template<int Index, int Begin, int End, int Size, class... Ts>
struct transform_apply<Index, Begin, End, Size, std::tuple<Ts...>>{
   static decltype(auto) apply(std::tuple<Ts...> tup)
   {
        auto val = std::get<Index>(tup);
        return std::tuple_cat(std::make_tuple(val), transform_apply<Index+1, Begin, End, Size, decltype(tup)>::apply(tup));
   };
};

template<int Begin, int End, class... Ts, class... Fns>
decltype(auto) transform(std::tuple<Ts...> tup, Fns... fns)
{
    return transform_apply<0, Begin, End, sizeof...(Ts), decltype(tup), Fns...>::apply(tup, fns...);
}

解释

我们基本上需要遍历元组,如果我们的索引在指定的范围内,则应用函数,否则不应用函数。该函数的结果被预先设置为下一个调用结果的元组(tuple_cat,你保存了我)。

利用C ++ 17&#39; constexpr if使得检查非常好。

上面的代码递归迭代,当我们的索引与元组的大小(结束迭代)相同时进行专门化。请注意,我们必须在指定的&#34; end&#34;之后继续迭代。这样我们就可以获得其余的元组。

这段代码可能会制作太多副本(就像我说的那样,它不是一种生产质量的解决方案)。

auto record = std::make_tuple(102, "alice", 2000, false);
auto nameToUpper = [](const char* name) {
    std::string cp(name); 
    cp[0] = static_cast<char>(toupper(cp[0])); 
    return cp;
};
auto ageTest = [](const int& age) {
    return (age < 0 || age > 100) ? -1 : age;
};
auto transformed_record = transform<1, 3>
(   record, 
    nameToUpper,
    ageTest
);
print(record);
print(transformed_record);

输出

(我使用my own answer for how to print a tuple来实现print):

(102, alice, 2000, 0)
(102, Alice, -1, 0)