递归调用重载的c ++函数

时间:2015-02-06 19:42:53

标签: c++ recursion sfinae

我有很少的自定义类型将用作函数的不同参数:

struct A {};
struct B {};
struct C {};
struct D {};
struct E {};

还有许多函数返回functor包装器:

template <typename H>
auto foo (H h,
  enable_if_t<is_same<typename result_of<H(A)>::type, bool>::value>* = 0)
{
  return [h] (B x) { return h (A {}); };
}

这件事将H(A)仿函数转换为G(B)仿函数,转换输入参数B-> A(为简单起见未在此处实现)并用A调用H.

我有类似的转换器C-> B,D-> C,E-> D:

template <typename H>
auto foo (H h,
  enable_if_t<is_same<typename result_of<H(B)>::type, bool>::value>* = 0)
{
  return [h] (C x) { return h (B {}); };
}

template <typename H>
auto foo (H h,
  enable_if_t<is_same<typename result_of<H(C)>::type, bool>::value>* = 0)
{
  return [h] (D x) { return h (C {}); };
}

template <typename H>
auto foo (H h,
  enable_if_t<is_same<typename result_of<H(D)>::type, bool>::value>* = 0)
{
  return [h] (E x) { return h (D {}); };
}

现在我可以调用foo 4次并获得带有#34; E&#34;类型参数的函子,最后用参数#34来调用内部处理程序; A&#34;:

auto inner_handler = [] (A) -> bool { return false; };
auto f = foo (foo (foo (foo ( inner_handler ))));
f (E {});

我想要的是实现call_until函数,该函数将调用&#34; foo&#34;递归重载,直到结果函子的参数类型变为T。

让我们说从A到E的转换器的路径总是存在而且恰好是一个。换句话说,我喜欢表达式

auto f = call_until<E> ( inner_handler );

完全按照

工作
auto f = foo (foo (foo (foo ( inner_handler ))));

我从以下内容开始:

template <typename Stop, typename Handler, typename Result>
struct call_until_helper
{
  Handler handler_;
  call_until_helper (Handler h) : handler (h) {}
};

template <typename Stop, typename Handler>
call_until_helper<Stop, Handler,
  typename boost::function_traits<Handler>::result_type>
call_until (Handler handler)
{
  return call_until_helper<Stop, Handler,
    typename boost::function_traits<Handler>::result_type> (handler);
}

但是我遇到了编译错误,有点卡住了。我需要一些想法来实现这一点。

在线代码:http://ideone.com/ZRFxnw

1 个答案:

答案 0 :(得分:3)

您的近似问题是function_traits需要函数类型,而不是lambda类型。要确定这一点,只需找到错误,提取导致它的语句,然后剥离类型并直接传递它们。

boost::function_traits< decltype( inner_handler ) >::result_type b = false;

无法编译。然后我检查了文档,是的,它需要一个函数类型,而不是一个lambda。 Lambda不是函数。


这是解决实际问题附近问题的草图。语法略有不同,因为我很懒。

这是fooify。它表示单个对象中foo的整个重载集:

struct fooify {
  template<class...Args>
  auto operator()(Args&&...args)const{
    return foo(std::forward<Args>(args)...);
  }
};

这是一种帮助器类型,在传递测试之前递归地将操作应用于输入:

template<class Action, template<class...>class test, class Arg, class=void>
struct repeat_until_helper {
  using action_result = result_of_t< Action&(Arg) >;
  auto operator()(Action&& action, Arg&&arg)const {
    return repeat_until_helper<Action, test, action_result>{}(
      std::forward<Action>(action),
      action( std::forward<Arg>(arg) )
    );
  }
};
template<class Action, template<class...>class test, class Arg>
struct repeat_until_helper< Action, test, Arg,
  std::enable_if_t< test<Arg>{} >
> {
  auto operator()(Action&& action, Arg&&arg)const {
    return std::forward<Arg>(arg);
  }
};

这是一个使用上面帮助器的函数,所以我们只需要传入一种类型(测试),其余的都是推导出来的:

template<template<class...>class test, class Action, class Arg>
auto repeat_until( Action&& action, Arg&& arg) {
    return repeat_until_helper< Action, test, Arg >{}( std::forward<Action>(action), std::forward<Arg>(arg) );
}

以下是使用E rvalue调用的类型的测试:

template<class X, class=void>
struct can_be_called_with_E:std::false_type{};
template<class X>
struct can_be_called_with_E<X,
    decltype(
        void(
            std::declval<X>()(std::declval<E>())
        )
    )
>:std::true_type{};

我们完成了。语法不同,但这只是一些整理工作。

live example