我正在编写一种方法来从任意嵌套的结构中提取值。我快到了,但还想提供一个选项来转换检索到的值(默认情况下不进行转换)。由于参数包后面不能跟另一个模板参数,所以我不得不稍微捏造一下。除指定行外,以下工作:
#include <iostream>
#include <type_traits>
typedef struct {
int a;
int b;
} bar;
typedef struct {
int c;
bar d;
} baz;
template <typename T, typename S, typename... Ss>
auto inline getField(const T& obj, S field1, Ss... fields)
{
if constexpr (!sizeof...(fields))
return obj.*field1;
else
return getField(obj.*field1, fields...);
}
template <typename Obj, typename Out, class ...C, typename... T>
auto inline getFieldC(const Obj& obj, Out, T C::*... field)
{
return static_cast<Out>(getField(obj, field...));
}
template<class T> struct tag_t { using type = T; };
template<class...Ts>
using last = typename std::tuple_element_t< sizeof...(Ts) - 1, std::tuple<tag_t<Ts>...> >::type;
template <typename Obj, typename... T>
auto getMyFieldWrapper(const Obj& obj, T... field)
{
if constexpr (std::is_member_object_pointer_v<last<Obj, T...>>)
return getField(obj, field...);
else
return getFieldC(obj, last<Obj, T...>{}, field...); // <- this doesn't compile, need a way to pass all but last element of field
}
int main()
{
baz myObj;
std::cout << getMyFieldWrapper(myObj, &baz::c); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b); // works
std::cout << getMyFieldWrapper(myObj, &baz::d, &bar::b, 0.); // doesn't work
}
如何实现指定的行?我正在使用最新的 MSVC,并且很高兴充分利用 C++17 来保持简短。
最佳答案
通常对反转流更有帮助。首先,编写一个转发索引序列的高阶函数:
template <typename F, size_t... Is>
auto indices_impl(F f, std::index_sequence<Is...>) {
return f(std::integral_constant<size_t, Is>()...);
}
template <size_t N, typename F>
auto indices(F f) {
return indices_impl(f, std::make_index_sequence<N>());
}
这在很多地方通常都很有用。
在这种情况下,我们用它来编写一个高阶函数来删除包中的最后一个元素:
template <typename F, typename... Ts>
auto drop_last(F f, Ts... ts) {
return indices<sizeof...(Ts)-1>([&](auto... Is){
auto tuple = std::make_tuple(ts...);
return f(std::get<Is>(tuple)...);
});
}
然后你可以使用它:
return drop_last([&](auto... elems){
return getMyField(obj, last<Obj, T...>{}, elems...);
}, field...);
为简洁起见省略了引用。
当然,如果你想把两者结合起来,只是旋转,你可以这样做:
// Given f and some args t0, t1, ..., tn, calls f(tn, t0, t1, ..., tn-1)
template <typename F, typename... Ts>
auto rotate_right(F f, Ts... ts) {
auto tuple = std::make_tuple(ts...);
return indices<sizeof...(Ts)-1>([&](auto... Is){
return f(
std::get<sizeof...(Ts)-1>(tuple),
std::get<Is>(tuple)...);
});
}
用作:
return rotate_right([&](auto... elems){
return getMyField(obj, elems...);
}, field...);
关于c++ - 从函数参数包中删除最后一项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51993926/