【发布时间】:2020-07-14 20:07:30
【问题描述】:
我正在尝试构建一个 Filter 类型,它可以充当可组合的错误处理可调用对象(即带有 >= 的 Maybe monad),它(理想情况下)适用于(至少)lambda 和 @ C++17 中的 987654323@(无需与旧标准兼容)。理想情况下,可以这样工作:
// two example Filter's instantiated from lambda's
Filter g = [](const int a, const int b) -> float {
return a + b;
};
Filter f = [](const float c) -> std::optional<int> {
if c < 0 return std::nullopt;
else return c + 1;
};
// compose them - only two right now for brevity
auto h = f << g; // so that this implements f(g(...))
// and then later call them with some arguments
h(1.0, 2.0);
如果任何中间函数返回std::nullopt,我需要整个组合返回std::nullopt,并且它可以处理任意数量的组合函数(可能返回也可能不返回可选)。
到目前为止,我的实现(受this post 启发)在组合两个简单函数时适用于std::function,但在组合多个更改返回类型是否可选的函数时经常失败,因为类型推导非常脆弱。
#include <functional>
#include <optional>
#include <type_traits>
template <typename TReturn, typename... TArgs>
class Filter {
public:
/**
* The filter function that we evaluate.
*/
std::function<TReturn(TArgs...)> eval_;
/**
* Construct a filter from a std::function.
*/
Filter(std::function<TReturn(TArgs...)> f) : eval_(f) {}
/**
* Apply the filter to given arguments.
*/
auto operator()(TArgs... args) const {
return this->eval_(args...);
}
/**
* Compose this function, `f`, and `g`.
*/
template <typename TOReturn, typename... TOArgs>
auto operator<<(Filter<TOReturn, TOArgs...> other) const -> Filter<TReturn, std::optional<TOArgs>...> {
// the result of the resulting function f(g(...))
using TFReturn = std::optional<typename TReturn::value_type>;
// the type of the resulting function f(g(...))
using TFuncType = std::function<TFReturn(std::optional<TOArgs>...)>;
// construct (and return) the composed function
TFuncType f = [this, other](std::optional<TOArgs>... args) -> TFReturn {
// if we got a good value, perform the function composition
if ((args && ...)) {
// evaluate g over the input arguments
auto gresult = other(*(args)...);
// if we got a non-fail result from g, then call f
if (gresult) {
// and evaluate our own function over the result
return this->eval_(gresult);
}
} // END: if ((args && ...))
// if anything falls through, return failur
return std::nullopt;
};
return f;
} // END: operator<<
}; // END: class Filter
auto main() -> int {
// a couple of test functions
std::function<int(float, float)> f1 = [](const float a, const float b) -> int {
return a * b;
};
std::function<std::optional<int>(int)> f2 = [](const int c) -> std::optional<int> {
if (c < 0) return std::nullopt;
else return c;
};
std::function<std::optional<int>(int)> f3 = [](const int d) -> std::optional<int> {
if (d > 10) return 10;
else return d;
};
// these construct fine if I have explicitly typed std::function's above.
Filter f = f3;
Filter g = f2;
Filter h = f1;
// build the composition
auto F = g << h; // this works!
// auto F = f << g << h; // this does not work
// evaluate our composition over different arguments
auto x = F(1.0, 2.0);
auto y = F(-1.0, 2.0);
}
组合两个简单的函数 f << g 可以工作,但 f << g << h 当前失败并出现以下错误:
filter.cpp: In instantiation of ‘Filter<TReturn, std::optional<TOArgs>...> Filter<TReturn, TArgs>::operator<<(Filter<TOReturn, TOArgs ...>) const [with TOReturn = std::optional<int>; TOArgs = {int}; TReturn = std::optional<int>; TArgs = {int}]’:
filter.cpp:98:17: required from here
filter.cpp:55:43: error: no match for call to ‘(const std::function<std::optional<int>(int)>) (std::optional<int>&)’ 55 | return this->eval_(gresult); | ~~~~~~~~~~~^~~~~~~~~
In file included from /usr/include/c++/10/functional:59, from filter.cpp:1:
/usr/include/c++/10/bits/std_function.h:617:5: note: candidate: ‘_Res std::function<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) const [with _Res = std::optional<int>; _ArgTypes = {int}]’ 617 | function<_Res(_ArgTypes...)>:: | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/10/bits/std_function.h:618:25: note: no known conversion for argument 1 from ‘std::optional<int>’ to ‘int’ 618 | operator()(_ArgTypes... __args) const | ~~~~~~~~~^~~~~~~~~~
/usr/include/c++/10/bits/std_function.h:601:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = Filter<TReturn, TArgs>::operator<< <std::optional<int>, {int}>::<lambda(std::optional<int>)>; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Res = std::optional<int>; _ArgTypes = {std::optional<int>}]’, declared using local type ‘Filter<TReturn, TArgs>::operator<< <std::optional<int>, {int}>::<lambda(std::optional<int>)>’, is used but never defined [-fpermissive] 601 | function<_Res(_ArgTypes...)>::
如何修改类型推导以支持任意组合中的std::optional<T>(...) 和T(...) 函数?还是有一种根本不同的方法来解决这个问题?
【问题讨论】:
-
auto operator<<(Filter<TOReturn, TOArgs...> other) const -> Filter<TReturn, std::optional<TOArgs>...> {--> TReturn 是不是打错字了? -
@KorelK 否 -
f(g(...)的返回类型是f的返回类型,即TReturn。所以,f(g(...))是来自TOArgs -> TReturn的函数 -
对不起,错过了类上面的类型。
-
你想让
h(1.0, 2.0)是什么意思?g是一元的,但您要向它传递两个参数……然后f是二进制的,您要向它传递一个参数。这只是倒退吗? -
@巴里。抱歉 - 我在提供示例时得到了 f 的顺序并且不正确(注意:在提供的示例代码中它是正确的)。我已经更新了问题,很抱歉造成混淆!