c++ - 消除 C++ 中多余的模板参数

标签 c++ templates functional-programming

我正在尝试编写一个实现 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>map<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)>;
作为模式匹配器。它们不是模式匹配器,即使在特定情况下它们可以用作模式匹配器。 CallbackFun类型橡皮擦 s。Callback<X>接受任何可以用可以从 X 转换的东西调用的东西,并存储它。然后几乎忘记了关于它的所有其他事实(好吧,它记住了如何复制它、它的 typeid 以及其他一些随机事实)。Fun<X,Y>接受任何可以用可以从 X 转换的东西调用的东西,然后可以将其返回值转换为 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;
}
Live example .
我仍然认为您正在遵循悬空指针,但我不确定。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按值(value)。

关于c++ - 消除 C++ 中多余的模板参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65345054/

相关文章:

c++ - std::ref 和 swap 函数似乎不能很好地协同工作

templates - 在哪里定义可以在模板助手中重用的 Meteor 助手函数

javascript - AngularJS stateprovider View 模板

javascript - 有没有办法使用 ramda 将参数传递给 JavaScript 中的谓词?

javascript - 替换模型是近似 JavaScript 如何评估纯代码的好方法吗?

haskell - 存在函数依赖关系时类型推断如何工作

c++ - 如何遍历类的实例集合并访问其模板成员

c++ - std::map 字符串到 boost::thread_specific_ptr

c++ - error C2248,这是什么错误,我该如何解决?

c++ - wxdev-C++ GDIplus问题