c++ - 消除 C++ 中的冗余模板参数
问题描述
我正在尝试编写一个fmap
在 Haskell中实现的演示,continuation
我的代码如下所示:
#include <cstdio>
#include <functional>
template <typename X>
using Callback = std::function<void(X)>;
template <typename X, typename Y>
using Fun = std::function<Y(X)>;
template <typename X, typename Y>
struct F_map;
template <typename X>
struct __F {
virtual void operator()(Callback<X>&& callback) = 0;
virtual __F<X>* self() { return this; }
template <typename Y>
auto map(Fun<X, Y>&& f) { return F_map(self(), f); }
};
template <typename X>
struct F_id : __F<X> {
const X x;
F_id(const X& x) : x(x) {}
__F<X>* self() override { return this; }
void operator()(Callback<X>&& callback) override { callback(x); }
};
template <typename X, typename Y>
struct F_map : __F<Y> {
__F<X>* upstream;
Fun<X, Y> f;
F_map(__F<X>* upstream, const Fun<X, Y>& f) : upstream(upstream), f(f) {}
__F<Y>* self() override { return this; }
void operator()(Callback<Y>&& callback) override {
upstream->operator()([=](X&& x) {
callback(f(x));
});
}
};
int main(int argc, char* argv[]) {
auto f =
F_id(10)
.map<int>([](int x) { return x + 2; })
.map<const char*>([](int x) { return "1, 2, 3"; });
f([](const char* x) { printf("%s\n", x); });
return 0;
}
这很好用,但是map<int>
andmap<const char*>
看起来很难看。我认为这些声明可以省略,但是如果我删除它,我会收到一条错误消息,上面写着“没有函数模板的实例“F_id::map [with X=int]”与参数列表匹配”。
有什么想法可以删除这些模板参数吗?
解决方案
C++中有多种多态性。多态性是指代码中的单个变量具有不同的实现类型。
有经典的 C++ 继承和基于虚拟的多态性。有基于类型擦除的多态性。并且存在模板的静态多态性。
在许多意义上,这些多态性是相互对立的。如果你在应该使用另一个的时候使用一个,这就像在你应该使用逆变的时候使用协方差一样。你的代码可能会绊倒,但它只有在被迫时才会起作用,比如拿一个方形钉子、一个圆孔和一个大锤子。
您的<int>
要求是使用错误类型的多态性的示例,并且<int>
是锤子将其砸入错误形状的孔中。
您正在尝试使用
template <typename X>
using Callback = std::function<void(X)>;
和
template <typename X, typename Y>
using Fun = std::function<Y(X)>;
作为模式匹配器。它们不是模式匹配器,即使在特定情况下它们可以用作模式匹配器。 Callback
并且Fun
是类型橡皮擦。
Callback<X>
接受任何可以用可以从 转换的东西来调用的东西X
,并存储它。然后几乎忘记了关于它的所有其他事实(嗯,它记得如何复制它、它的 typeid 和一些其他随机事实)。
Fun<X,Y>
接受可以用可以从 a 转换的东西调用的任何东西,X
然后可以将其返回值转换为 a Y
。然后它几乎忘记了关于它的所有其他事实。
这里:
template <typename Y>
auto map(Fun<X, Y>&& f) { return F_map(self(), f); }
你试图用它来表达“我接受一个f
。请给我找一个Y
匹配这个的f
”。
这是模式匹配。类型擦除和模式匹配是相反的操作。
这是一个非常常见的错误。对于经典继承,它们有时最终会是同一件事。
std::function
是为了忘记有关某事的信息,能够存储它,然后只使用您记得的部分。
第一个问题是,你需要模式匹配,还是这里需要一个类型函数?
可能你很擅长类型函数。
template <class F, class R = std::invoke_result_t<F, X>>
F_map<X,R> map(F&& f) { return {self(), std::forward<F>(f)}; }
这里我们将传入映射F
到它的返回值R
。
您的代码还有其他问题。就像悬空指针一样。此外,它坚持知道可调用对象使用什么类型;在 C++ 中,你可以……只是不知道这一点。
因此,将 CRTP 用于静态多态性,并机械地忘记了我正在处理的类型,并用非类型擦除代码替换它们,我得到:
#include <cstdio>
#include <type_traits>
template <class Upstream, class F>
struct F_map;
template<class D>
struct mappable
{
template <class F>
F_map<D, F> map(F const& f) { return F_map(static_cast<D*>(this), f); }
};
template <class Upstream, class F>
struct F_map:
mappable<F_map<Upstream, F>>
{
Upstream* upstream;
F f;
F_map(Upstream* upstream, const F& f) : upstream(upstream), f(f) {}
template<class Callback>
void operator()(Callback&& callback) {
(*upstream)([=](auto&& x) {
callback(f(decltype(x)(x)));
});
}
};
template <typename X>
struct F_id:
mappable<F_id<X>>
{
const X x;
F_id(const X& x) : x(x) {}
template<class Callback>
void operator()(Callback&& callback) { callback(x); }
};
int main(int argc, char* argv[]) {
auto f =
F_id(10)
.map([](int x) { return x + 2; })
.map([](int x) { return "1, 2, 3"; });
f([](const char* x) { printf("%s\n", x); });
return 0;
}
我仍然认为您正在关注悬空指针,但我不确定。
的返回值map
存储了一个指向我们调用它的对象的指针,并且该对象在我们创建时被临时销毁f
。
为了解决这个Upstream*
问题,我会这样做:
template <class Upstream, class F>
struct F_map;
template<class D>
struct mappable
{
template <class F>
F_map<D, F> map(F const& f) const { return {*static_cast<D const*>(this), f}; }
};
template <class Upstream, class F>
struct F_map:
mappable<F_map<Upstream, F>>
{
Upstream upstream;
F f;
F_map(Upstream const& upstream, const F& f) : upstream(upstream), f(f) {}
template<class Callback>
void operator()(Callback&& callback) const {
upstream([=](auto&& x) {
callback(f(decltype(x)(x)));
});
}
};
template <typename X>
struct F_id:
mappable<F_id<X>>
{
const X x;
F_id(const X& x) : x(x) {}
template<class Callback>
void operator()(Callback&& callback) const { callback(x); }
};
按值复制upstream
。
推荐阅读
- javascript - 如何在管道中注入服务并返回字符串值?
- ios - iOS 上的应用内购买不会在 Swift 4 中获取产品
- python - 如何在 selenium 中使用多行脚本来执行脚本
- mongodb - MongoDB 健康检查失败 org.springframework.boot.actuate.mongo.MongoHealthIndicator"
- c - char * 和 void * 的对象表示
- html - Angular:无法在 PrimeNG FlexGrid 中并排定位两个 p-col-6
- android - 使用过滤结果更新列表时,Recyclerview 部分标题不可见
- reactjs - 在 useEffect 中使用异步函数的首选方式
- php - 如何用 preg_replace 或 str_replace 的自定义文本替换 div 中的文本?
- javascript - 登录成功时token已经存在,如何移动到主页?