成员变量指针的函数参数包

时间:2018-08-22 22:50:45

标签: c++ c++11

我正在尝试通过嵌套成员指针访问结构中的变量:

#include <iostream>

typedef struct {
    int a;
    int b;
} bar;

typedef struct {
    int c;
    bar d;
} baz;

template <typename obj, class C1, class C2, typename T1, typename T2>
T2 test1(const obj& obj_, T1 C1::* field1_, T2 C2::* field2_)
{
    return (obj_.*field1_).*field2_;
}

int main()
{
    baz myObj;
    test1(myObj, &baz::d, &bar::b);
}

我如何将函数测试转换为可变参数函数,以便我可以在结构的“深度”变量处访问变量? 我尝试按照功能参数列表部分here中的第二个示例进行操作,但似乎并没有得到它:

template <typename obj, class ...C, typename... T>
void test2(const obj& obj_, T C...::* field_)
{
    // ??
    // and what about the function return parameter?
}

int main()
{
    baz myObj;
    test2(obj,&baz::d,&bar::b);
    test2(obj,&baz::c);
}

因此,test2()的定义尚未编译。 可以使用任何(最新)C ++版本(尽管与MSVC一起使用)。 出于测试目的,这里是complete program on coliru


解决方案

由于Silvio's answer,我得以解决。利用C ++ 17,它可以做得更短:

template <typename T, typename S, typename... Ss>
auto inline test2(const T& obj, S field1, Ss... fields)
{
    if constexpr (!sizeof...(fields))
        return obj.*field1;
    else
        return test2(obj.*field1, fields...);
}

2 个答案:

答案 0 :(得分:1)

也许有一种更清洁的方法来执行此操作,但是您当然可以采用C ++模板非常喜欢的“扔在墙上,看看有什么东西”的方法。

template <typename T>
auto test2(const T& obj) -> T {
  return obj;
}

template <typename T, typename S, typename... Ss>
auto test2(const T& obj, S field1, Ss... fields)
  -> decltype(test2(obj.*field1, fields...)) {
  return test2(obj.*field1, fields...);
}

基本情况非​​常简单。如果我们不传递任何字段,则只返回原始对象本身。递归的情况就是这样:我们递归。返回类型声明为...返回值的声明类型。参数类型只是变量。它们将根据需要完全实例化。如果您传递没有意义的参数或不键入check,则会收到一些非常丑陋的错误消息。

答案 1 :(得分:1)

这需要支持折叠表达式。

namespace utils {
  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<class Lhs, class F>
  struct fold_invoker_t;
  template<class Lhs, class F>
  fold_invoker_t<Lhs, F> fold_invoker(Lhs&&lhs, F&& f);

  template<class Lhs, class F>
  struct fold_invoker_t {
    Lhs lhs;  
    F f;
    template<class Rhs>
    auto operator*( Rhs&& rhs )&& {
      return fold_invoker(std::forward<F>(f)(std::forward<Lhs>(lhs), std::forward<Rhs>(rhs)), static_cast<F>(f));
    }
  };
  template<class Lhs, class F>
  fold_invoker_t<Lhs, F> fold_invoker(Lhs&&lhs, F&& f){ return {std::forward<Lhs>(lhs), std::forward<F>(f)}; }
}  

然后我们写:

template <typename Obj, class ...C, typename... T>
utils::last<Obj, T...> const& test2(const Obj& obj, T C::*... field)
{
  auto get_member=[](auto&& elem, auto&& memptr)->decltype(auto){ return elem.*memptr; };
  return (utils::fold_invoker( obj, get_member ) * ... * field).lhs;
}

并在那一行上全部展开。

live example

不知道这是否可以在MSVC的C ++ 17支持下工作。