【问题标题】:Recursive calling overloaded c++ functions递归调用重载的 C++ 函数
【发布时间】:2015-02-06 19:42:53
【问题描述】:

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

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

还有许多返回函子包装的函数:

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 次,将得到获取类型为“E”的参数的函子,最后用参数“A”调用内部处理程序:

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

我想要的是实现 call_until 函数,该函数将递归调用“foo”重载,直到结果函子的参数类型变为 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

【问题讨论】:

  • 尝试使用-&gt; 来表示“从左侧获取参数,并返回右侧”,因为这匹配更传统的函数符号。所以有一个过载foo:H:A-&gt;bool-&gt;G:B-&gt;bool。为了让你的代码更干净,你可以用result_of_t替换result_of吗?如果你的编译器没有,写一个template&lt;class Sig&gt;using result_of_t=typename std::result_of&lt;Sig&gt;::type;。噪音越小越好。

标签: c++ recursion sfinae


【解决方案1】:

您的最接近的问题是 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);
  }
};

这是一个使用上述helper的函数,所以我们只需要传入一个类型(测试),其余的推导即可:

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 右值调用的类型的测试:

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

【讨论】:

  • 您的代码运行良好,可以轻松移植到 c++11。但是,当我尝试将其移植到 c++03(使用 boost 元库、类型特征等)时,我遇到了困难。特定的问题是无法为编译器定义“fooify”函子的返回类型。这是我将其移植到 c++03 的最佳尝试:coliru.stacked-crooked.com/a/f8b40e6443be9c9e 不幸的是它无法编译 :)
  • @NikkiChumakov 是的,C++03 中的重载集对象很难写。您可以将Action 替换为对repeat_until_helper 中的foo 的硬编码调用(并将其称为repeat_foo_until_helper)。但是 action_result 最终会变得一团糟,因为您缺少 decltype 或任何等效项,并且您缺少类型列表以使手动限制 decltype 可行。
  • 最后我能够通过手动将 foo 的返回结果特征定义为“模板 struct foo_traits::type, bool>>::type" 对于 [A,B,C,D,E] 中的每个 Arg。我在 footify 类中使​​用了这些特征。这很丑陋,但以某种方式起作用。感谢您的帮助。
  • @Nikki 顺便说一句,你知道你可以有一个从E 写入can_be_called_with_E 的模板吗? template&lt;class E, class X,class=void&gt; struct helper:std::false_type{}; template&lt;class E, class X&gt;struct helper&lt;X,decltype(void(std::declval&lt;X&gt;()(std::declval&lt;E&gt;()))&gt;:std::true_type{}; 然后 template&lt;class E&gt;struct can_be_called_with{ template&lt;class X&gt; struct test: helper&lt;E,X&gt;{}; }; 在 C++03 中没有用,但是..
  • Boost 具有“result_of”功能,支持open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1454.html 中定义的结果解析方法,所以我只是使用它。我需要在我的所有功能对象中定义嵌套的“结果”模板类,但由于我必须编写与 c++03 兼容的代码,所以我通常还是这样做。这是代码粘贴:codepad.org/y2kDHnM3。这是完整的 c++03 端口,仅供记录:coliru.stacked-crooked.com/a/77671d38349920da
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 2014-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多