传递具有模板化 return 类型的 std::function 类型
Passing std::function type which has a templated return type
我有一个功能
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 的两个元素和 return 类型 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,
现在,如果我将其更改为使用特定的 return 类型 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
但是我想确保如果有人创建了特定于某些类型的 functor/lambda,那么该函数会处理正确的类型。那么为什么声明不能编译呢?
编辑:已找到完整示例 https://github.com/meconlen/stringAlgorithms/tree/soq
要调用该函数,您需要实际给它一个 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));
否则,编译器的转换太多,无法弄清楚您要做什么。
根据 Piotr Skotnicki 提供的建议,我为您获得了以下可能的解决方案。您希望 C++ 推断 score_function
本身的类型,而不是它的嵌套类型。但是,一旦您拥有该类型 - 让我们称之为 F
,您就可以手动检索所需的内容。
此辅助特征结构从您的代码中针对给定的功能类型 F
提取 P
(这基本上是 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 - 它应该适用于所有情况。
简要说明。当你写:
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
,因为:
- 还在推导语境中。
- 参数类型 (
char(*)()
) 与参数类型不匹配 std::function<P()>
。
也就是说,编译器不知道 std::function<P()>
签名中的 P
应该与函数指针的 return 类型相匹配,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
return 是否可以转换为 P
,或者只读那个类型。
在您的场景中,完全不相关的类型 (plus_minus_one
) 的实例作为 std::function
的实例传递,很明显,推导失败。编译器不知道它必须查看其 operator()
,并使用其默认的 return 类型。
std::function
本身就是一个类型橡皮擦,在你的情况下你真的不需要擦除任何类型。
我有一个功能
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 的两个元素和 return 类型 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,
现在,如果我将其更改为使用特定的 return 类型 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
但是我想确保如果有人创建了特定于某些类型的 functor/lambda,那么该函数会处理正确的类型。那么为什么声明不能编译呢?
编辑:已找到完整示例 https://github.com/meconlen/stringAlgorithms/tree/soq
要调用该函数,您需要实际给它一个 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));
否则,编译器的转换太多,无法弄清楚您要做什么。
根据 Piotr Skotnicki 提供的建议,我为您获得了以下可能的解决方案。您希望 C++ 推断 score_function
本身的类型,而不是它的嵌套类型。但是,一旦您拥有该类型 - 让我们称之为 F
,您就可以手动检索所需的内容。
此辅助特征结构从您的代码中针对给定的功能类型 F
提取 P
(这基本上是 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 - 它应该适用于所有情况。
简要说明。当你写:
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
,因为:
- 还在推导语境中。
- 参数类型 (
char(*)()
) 与参数类型不匹配std::function<P()>
。
也就是说,编译器不知道 std::function<P()>
签名中的 P
应该与函数指针的 return 类型相匹配,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
return 是否可以转换为 P
,或者只读那个类型。
在您的场景中,完全不相关的类型 (plus_minus_one
) 的实例作为 std::function
的实例传递,很明显,推导失败。编译器不知道它必须查看其 operator()
,并使用其默认的 return 类型。
std::function
本身就是一个类型橡皮擦,在你的情况下你真的不需要擦除任何类型。