【问题标题】:Constraint template parameter depending on passed functor根据传递的函子约束模板参数
【发布时间】:2017-12-22 21:52:57
【问题描述】:

我想根据传递的函子来约束模板参数。考虑这个来自某些容器类型的FoldLeft 函数:

template<typename F, typename R>
R FoldLeft(F&& functor, R initialValue) {
    R r = initialValue;
    /* assume that this is a range of uint64_t's */
    while (first != last) {
        r = std::forward<F>(functor)(r, *(first++));
    }
    return r;
}

这个函数可以这样调用:

auto sum = FoldLeft([](uint64_t i, auto& e) { return e + i; }, 0);

这里的问题是R 是从initialValue 参数推导出来的,在这种情况下是0,因此导致int。同样decltype(sum) 也给出int

我想将R 推导出为函子的返回类型,它可以是 lambda 或任何其他可调用类型。我已经尝试过使用this answer的方法,但总是遇到这个错误:

error:  decltype cannot resolve address of overloaded function
struct function_traits
       ^~~~~~~~~~~~~~~
note:   substitution of deduced template arguments resulted in errors seen above

我尝试的代码(fuction_traits 复制自链接答案):

template<typename T>
using LamRet = typename function_traits<T>::result_type;

template<typename F>
LamRet<F> FoldLeft(F&& functor, LamRet<F> initialValue) {
    LamRet<F> r = initialValue;
    /* assume that this is a range of uint64_t's */
    while (first != last) {
        r = std::forward<F>(functor)(r, *(first++));
    }
    return r;
}

【问题讨论】:

  • first/last获取这些信息比按照你的建议去做要容易得多。
  • 如果您不使用 auto 作为 lambda 中的参数也没问题,您的代码将正常工作

标签: c++ c++11 templates lambda


【解决方案1】:

Function traits 所描述的,根据我的经验,几乎没用,并且使用它们会像这样绑定,因为 C++ 中的可调用对象没有像函数特征那样声称的特征。

(一个大的例外是您几乎在使用特定于问题的子语言并有意与特征合作以便调用 DRY 而不必在两个位置重复类型)。

只有一部分可调用对象才具有此类特征。而且,您编写的 C++14 和 C++17 样式 lambda 越多,符合条件的可调用对象就越少。

确定返回值,你需要知道你迭代的类型是什么。然后检查decltype( lambda( argument, iterated_type ) )(也可以写成result_of_tinvoke_result_t模板类型)。

假设你的迭代类型是T,你的参数是A

template<class F, class A>
using LamRet = std::decay_t<std::result_of_t<F&&( A&&, T& )>>;

然后我们可以检查我们的 lambda 参数类型:

template<class F, class A>
using LamArgGood = std::is_convertible< A&&, LamRet<F, A> >;

template<class F, class A>
using LamRetGood = std::is_convertible< LamRet<F, A>, LamRet< F, LamRet<F, A > >;

确保迭代的返回类型有效。

template<class F, class A,
  class dA = std::decay_t<A>,
  std::enable_if_t< LamArgGood<F, dA>{} && LamRetGood<F, dA>{}, bool> =true
>
LamRet<F, dA> FoldLeft(F&& functor, A&& initialValue) {
  LamRet<F, dA> r = std::forward<A>(initialValue);
  /* assume that this is a range of uint64_t's */
  while (first != last) {
    r = std::forward<F>(functor)(r, *(first++));
  }
  return r;
}

这不太对,但会捕获 99% 的类型错误。 (我在迭代中分配,而不是构造;我从A&amp;&amp; 转换为LamRet,而不是dA&amp;&amp;)。

【讨论】:

  • 很好的答案,您介意详细说明导致 1% 的类型错误未被捕获的原因吗?
  • @nyron 我做了吗?示例在同一段落的括号中。
  • 或者你想要更多的单词?不可赋值但可构造的类型被迭代;这很容易修复(在一个地方用可分配的特征替换 domvertible 特征)。加上带有返回类型的奇怪复制/移动构造函数操作的参数。我将不得不尝试找出一个上面会失败的例子。包括自身的返回类型。如果返回类型 binop 本身出现了一些奇怪的情况,我会忽略这个事实,只要它转换为返回类型(一次迭代的选择是任意的)
  • 谢谢你,现在你在说什么很清楚了。
猜你喜欢
  • 2021-09-04
  • 2021-06-29
  • 2018-08-19
  • 2021-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-13
相关资源
最近更新 更多