是否可以 return 来自函数模板的可变 lambda?
Is it possible to return a variadic lambda from a function template?
我有以下代码 (c++11):
template <typename F,
typename FirstT,
typename... FIn>
auto min_on(F f, FirstT first, FIn... v) -> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
using f_rettype = decltype(f(first));
rettype result = first;
f_rettype result_trans = f(first);
f_rettype v_trans;
(void)std::initializer_list<int>{
((v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
: 0)...};
return result;
}
基本上 returns 参数 result
产生了表达式 f(result)
的最小值。可以这样调用:
auto mod7 = [](int x)
{
return x % 7;
};
auto minimum = min_on(mod7, 2, 8, 17, 5);
assert( minimum == 8); // since 8%7 = 1 -> minimum value for all arguments passed
现在我想以 'curried' 的方式使用它,这样我就可以从 min_on
得到一个可变的 lambda,然后用参数调用它(我可能稍后会收到),就像这样:
auto mod7 = [](int x)
{
return x % 7;
};
auto f_min = min_on(mod7);
auto minimum = f_min(2, 8, 17, 5);
// or
auto minimum = min_on(mod7)(2, 8, 17, 5);
这可能吗?
在 C++11 中,如果您愿意手动创建函数对象,则可以执行以下操作:
template <typename F>
struct min_on_t {
min_on_t(F f) : f(f) {}
template <typename T, typename... Ts>
auto operator ()(T x, Ts... xs) -> typename std::common_type<T, Ts...>::type
{
// Magic happens here.
return f(x);
}
private: F f;
};
template <typename F>
auto min_on(F f) -> min_on_t<F>
{
return min_on_t<F>{f};
}
然后调用它:
auto minimum = min_on(mod7)(2, 8, 17, 5);
要在 C++14 中使用 lambda,您需要省略尾随的 return 类型,因为如果不先将其分配给变量,则无法指定 lambda 的类型,因为 a lambda expression cannot occur in an unevaluated context。
template <typename F>
auto min_on(F f)
{
return [f](auto x, auto... xs) {
using rettype = std::common_type_t<decltype(x), decltype(xs)...>;
using f_rettype = decltype(f(x));
rettype result = x;
f_rettype result_trans = f(x);
(void)std::initializer_list<int>{
(f(xs) < result_trans
? (result = static_cast<rettype>(xs), result_trans = f(xs), 0)
: 0)...};
return result;
};
}
在 C++11 上不确定,但在 C++14 中,您可以创建一个 lambda 来包装您的函数:
auto min_on_t = [](auto f) {
return [=](auto ... params) {
return min_on(f, params...);
};
};
auto min_t = min_on_t(mod7);
auto minimum = min_t(2, 8, 17, 5);
在 C++14 中这很容易。
template<class F>
auto min_on( F&& f ) {
return [f=std::forward<F>(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
许多编译器在完全支持 C++14 之前得到了 auto
return lambda 中的类型推导和参数。所以名义上的 C++11 编译器可能能够编译这个:
auto min_on = [](auto&& f) {
return [f=decltype(f)(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
在 C++11 中:
struct min_on_helper {
template<class...Args>
auto operator()(Args&&...args)
-> decltype( min_on_impl(std::declval<Args>()...) )
{
return min_on_impl(std::forward<Args>(args)...);
}
};
是样板文件。这让我们可以将 min_on_impl
的整个重载集作为一个对象传递。
template<class F, class T>
struct bind_1st_t {
F f;
T t;
template<class...Args>
typename std::result_of<F&(T&, Args...)>::type operator()(Args&&...args)&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F const&(T const&, Args...)>::type operator()(Args&&...args)const&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F(T, Args...)>::type operator()(Args&&...args)&&{
return std::move(f)( std::move(t), std::forward<Args>(args)... );
}
};
template<class F, class T>
bind_1st_t< typename std::decay<F>::type, typename std::decay<T>::type >
bind_1st( F&& f, T&& t ) {
return {std::forward<F>(f), std::forward<T>(t)};
}
给我们 bind_1st
.
template<class T>
auto min_on( T&& t )
-> decltype( bind_1st( min_on_helper{}, std::declval<T>() ) )
{
return bind_1st(min_on_helper{}, std::forward<T>(t));
}
是模块化的,可以解决您的问题:min_on_helper
和 bind_1st
都可以独立测试。
您也可以将 bind_1st
替换为对 std::bind
的调用,但根据我的经验,std::bind
的怪癖让我在向任何人推荐它时非常谨慎。
我有以下代码 (c++11):
template <typename F,
typename FirstT,
typename... FIn>
auto min_on(F f, FirstT first, FIn... v) -> typename std::common_type<FirstT, FIn...>::type
{
using rettype = typename std::common_type<FirstT, FIn...>::type;
using f_rettype = decltype(f(first));
rettype result = first;
f_rettype result_trans = f(first);
f_rettype v_trans;
(void)std::initializer_list<int>{
((v_trans = f(v), v_trans < result_trans)
? (result = static_cast<rettype>(v), result_trans = v_trans, 0)
: 0)...};
return result;
}
基本上 returns 参数 result
产生了表达式 f(result)
的最小值。可以这样调用:
auto mod7 = [](int x)
{
return x % 7;
};
auto minimum = min_on(mod7, 2, 8, 17, 5);
assert( minimum == 8); // since 8%7 = 1 -> minimum value for all arguments passed
现在我想以 'curried' 的方式使用它,这样我就可以从 min_on
得到一个可变的 lambda,然后用参数调用它(我可能稍后会收到),就像这样:
auto mod7 = [](int x)
{
return x % 7;
};
auto f_min = min_on(mod7);
auto minimum = f_min(2, 8, 17, 5);
// or
auto minimum = min_on(mod7)(2, 8, 17, 5);
这可能吗?
在 C++11 中,如果您愿意手动创建函数对象,则可以执行以下操作:
template <typename F>
struct min_on_t {
min_on_t(F f) : f(f) {}
template <typename T, typename... Ts>
auto operator ()(T x, Ts... xs) -> typename std::common_type<T, Ts...>::type
{
// Magic happens here.
return f(x);
}
private: F f;
};
template <typename F>
auto min_on(F f) -> min_on_t<F>
{
return min_on_t<F>{f};
}
然后调用它:
auto minimum = min_on(mod7)(2, 8, 17, 5);
要在 C++14 中使用 lambda,您需要省略尾随的 return 类型,因为如果不先将其分配给变量,则无法指定 lambda 的类型,因为 a lambda expression cannot occur in an unevaluated context。
template <typename F>
auto min_on(F f)
{
return [f](auto x, auto... xs) {
using rettype = std::common_type_t<decltype(x), decltype(xs)...>;
using f_rettype = decltype(f(x));
rettype result = x;
f_rettype result_trans = f(x);
(void)std::initializer_list<int>{
(f(xs) < result_trans
? (result = static_cast<rettype>(xs), result_trans = f(xs), 0)
: 0)...};
return result;
};
}
在 C++11 上不确定,但在 C++14 中,您可以创建一个 lambda 来包装您的函数:
auto min_on_t = [](auto f) {
return [=](auto ... params) {
return min_on(f, params...);
};
};
auto min_t = min_on_t(mod7);
auto minimum = min_t(2, 8, 17, 5);
在 C++14 中这很容易。
template<class F>
auto min_on( F&& f ) {
return [f=std::forward<F>(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
许多编译器在完全支持 C++14 之前得到了 auto
return lambda 中的类型推导和参数。所以名义上的 C++11 编译器可能能够编译这个:
auto min_on = [](auto&& f) {
return [f=decltype(f)(f)](auto&& arg0, auto&&...args) {
// call your function here, using decltype(args)(args) to perfect forward
};
}
在 C++11 中:
struct min_on_helper {
template<class...Args>
auto operator()(Args&&...args)
-> decltype( min_on_impl(std::declval<Args>()...) )
{
return min_on_impl(std::forward<Args>(args)...);
}
};
是样板文件。这让我们可以将 min_on_impl
的整个重载集作为一个对象传递。
template<class F, class T>
struct bind_1st_t {
F f;
T t;
template<class...Args>
typename std::result_of<F&(T&, Args...)>::type operator()(Args&&...args)&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F const&(T const&, Args...)>::type operator()(Args&&...args)const&{
return f( t, std::forward<Args>(args)... );
}
template<class...Args>
typename std::result_of<F(T, Args...)>::type operator()(Args&&...args)&&{
return std::move(f)( std::move(t), std::forward<Args>(args)... );
}
};
template<class F, class T>
bind_1st_t< typename std::decay<F>::type, typename std::decay<T>::type >
bind_1st( F&& f, T&& t ) {
return {std::forward<F>(f), std::forward<T>(t)};
}
给我们 bind_1st
.
template<class T>
auto min_on( T&& t )
-> decltype( bind_1st( min_on_helper{}, std::declval<T>() ) )
{
return bind_1st(min_on_helper{}, std::forward<T>(t));
}
是模块化的,可以解决您的问题:min_on_helper
和 bind_1st
都可以独立测试。
您也可以将 bind_1st
替换为对 std::bind
的调用,但根据我的经验,std::bind
的怪癖让我在向任何人推荐它时非常谨慎。