【问题标题】:Passing std::function type which has a templated return type传递具有模板化返回类型的 std::function 类型
【发布时间】:2016-03-19 17:08:35
【问题描述】:

我有一个函数

template<typename P = int_fast16_t, typename I>
std::vector<std::vector<P>> f(
    I xb, I xe, I yb, I ye,
    std::function<P(typename std::iterator_traits<I>::value_type,
                    typename std::iterator_traits<I>::value_type)> &&score_function,
    P ID = -1, P S = -1, P M = 1)

它需要两对迭代器和一个函数,该函数应该比较迭代器的 value_type 的两个元素并返回 P 类型的值。

这给了我一个错误

./h.hpp:47:32: note: candidate template ignored: could not match 'function<type-parameter-0-0 (typename iterator_traits<type-parameter-0-1>::value_type, typename iterator_traits<type-parameter-0-1>::value_type)>' against 'stringAlgorithms::scoring::plus_minus_one'
   std::vector<std::vector<P>> nw_score_matrix(I xb, I xe, I yb, I ye, 

现在,如果我将其更改为使用特定的返回类型 P

template<typename P = int_fast16_t, typename I>
   std::vector<std::vector<P>> nw_score_matrix(I xb, I xe, I yb, I ye, std::function<int_fast16_t(typename std::iterator_traits<I>::value_type, typename std::iterator_traits<I>::value_type)> &&score_function, P ID = -1, P S = -1, P M = 1)

这编译。

在这种情况下,函数 plus_minus_one 是

  struct plus_minus_one {
     template<typename T, typename R = int_fast16_t>
     R operator()(const T &x, const T &y) { return x == y ? 1 : -1; }
  };

并通过使用

scoring::plus_minus_one matchScoring;

nw_score_matrix(x.begin(), x.end(), y.begin(), y.end(), matchScoring);

我意识到我可以在模板中声明一个类型名 F 并将 score_function 设为

F &&score_function

但是我想确保如果有人创建了特定于某些类型的函子/lambda,则该函数处理正确的类型。那么为什么声明不编译呢?

编辑:找到一个完整的例子https://github.com/meconlen/stringAlgorithms/tree/soq

【问题讨论】:

  • 你有什么问题?
  • 声明确实可以编译,但 function&lt;...&gt; 中的内容是非推断上下文。带有typename X&lt;T&gt;::type 的任何内容都是参数声明中的非推断上下文。你最好用F&amp;&amp;
  • 您可以将score_function类型的所有成分类型都设为非推导(例如typename std::common_type&lt;P&gt;::type等)。
  • 任何不是std::function(并且不是从它派生出来的)都可以推导出为std::function。使用类似std::vector&lt;std::vector&lt;typename std::decay&lt;typename std::result_of&lt;F(typename std::iterator_traits&lt;I&gt;::value_type, typename std::iterator_traits&lt;I&gt;::value_type)&gt;::type&gt;::type&gt;&gt; nw_score_matrix(stuff, F&amp;&amp; f)

标签: c++ c++11 std-function


【解决方案1】:

要调用该函数,您实际上需要给它一个std::function

scoring::plus_minus_one matchScoring;
std::function<int_fast16_t(int,int)> score_function = matchScoring;
nw_score_matrix(x.begin(), x.end(), y.begin(), y.end(), std::move(score_function));

否则,编译器会进行太多转换,无法确定您要执行的操作。

【讨论】:

  • 我明白了,这是因为即使 P 具有默认类型,编译器仍试图从 score_function 推断 P,失败(因为返回类型不是函数签名的一部分?)并忽略默认值?
【解决方案2】:

简要说明。当你写:

template <typename P = int>
void f(std::function<P()>) {}

那么P 仍处于推断上下文中,这意味着编译器将尝试根据参数表达式的类型推断P,而忽略默认值(int)。但是类型推导只有推导类型,即实参表达式的类型必须与对应参数的类型相匹配,这样编译器才会推导缺失类型的模板形参.

所以,一旦你像下面这样调用函数:

std::function<float()> a;
f(a);

编译器会将P 替换为float

现在,如果您想传入,例如函数地址:

char foo() { return {}; }
f(&foo);

编译器会抱怨它不能推导出P,因为:

  1. 它仍处于推断的上下文中。
  2. 参数类型(char(*)())与参数类型std::function&lt;P()&gt;不匹配。

也就是说,编译器不知道std::function&lt;P()&gt; 的签名中的P 应该与函数指针的返回类型char 匹配。

比如说,你可以明确地强制类型:

f<char>(&foo);

但这不是很灵活。

您也可以将std::function 放在非推导上下文中,并让P 在别处推导:

template <typename T> struct identity { using type = T; };

template <typename P>
void f(typename identity<std::function<P()>>::type, P c) {}

f(&foo, 'a'); // P will be deduced from the 'a' argument

但是如果c 是默认的并且没有在函数调用参数列表中指定,这将不起作用。

没有什么能阻止你推断出参数表达式的确切类型:

template <typename F>
void f(F&& f) {}

f(&foo);

如果您关心,您可以随时检查 f 返回的任何内容是否可转换为 P,或者只是读取该类型。

在您的场景中,完全不相关类型 (plus_minus_one) 的实例作为std::function 的实例传递,因此很明显,推导失败。编译器不知道它必须查看它的operator(),并使用它的默认返回类型。

std::function 本身就是一个类型橡皮擦,在你的情况下,你真的不需要擦除任何类型。

【讨论】:

    【解决方案3】:

    使用 Piotr Skotnicki 提供的建议,我为您获得了以下可能的解决方案。您希望 C++ 推断 score_function 本身的类型,而不是它的嵌套类型。然而,一旦你有了那个类型——让我们称之为F,那么你就可以手动检索你需要的东西。

    此辅助特征结构从您的代码中提取P 给定功能类型F(这基本上是Piotr Skitnicki 的评论)

    template<typename F, typename I>
    struct PP {
        typedef typename std::decay<typename std::result_of<
          F(typename std::iterator_traits<I>::value_type,
            typename std::iterator_traits<I>::value_type)
          >::type>::type type;
    };
    

    现在,借助此帮助,您可以将函数定义为:

    template<typename I, typename F>
       std::vector<std::vector<typename PP<F,I>::type>>
         nw_score_matrix(I xb, I xe, I yb, I ye,
                         F &&score_function,
                         typename PP<F,I>::type ID = -1,
                         typename PP<F,I>::type S = -1,
                         typename PP<F,I>::type M = 1)
       {
           typedef typename PP<F,I>::type P;
    
           ....... your existing code ......
       }
    

    ... 然后您可以在调用站点调用该函数而无需进行任何更改。特别是,您不需要将matchScoring 转换为std::function 对象。

    我相信它符合您的要求,即 F 可能是函子、std::function 或 lambda - 它应该适用于所有情况。

    【讨论】:

    • 我想我现在看到了,struct PP 允许我推断出 F 的返回类型,我可以使用它而不是模板参数 P。我需要考虑一下我希望如何设计它。 Vaughn Cato 的回答解释了为什么我的示例没有编译,这是我最初追求的,但这让我考虑一个我认为可能更好的不同设计选项。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多