我有一个函数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
作为参数,两个索引funcs
和Begin
作为模板参数。函数End
将transform
中的每个函数应用于元组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
之间的元素,但即便如此,模板参数的排序仍然存在问题。
答案 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)