Maybe monad:如何避免在 Maybe<T> 实例中存储 T 类型的值?

Maybe monad: how to avoid storing T-typed value in Maybe<T> instance?

在下面的 coliru 中你会发现我对 "Maybe" monad 的实现。

http://coliru.stacked-crooked.com/a/82978c254410ba6e

我遇到的问题是 "Nothing" 值带有一个不必要的 T 类型的数据成员,就像确实需要它的 "Just" 值一样。

是否可以在 "Nothing" 值不与 "Just" 值一样大的情况下实施 Maybe<T> 并且不求助于动态分配 T 类型的值?

我尝试将 Just<T>Nothing<T> 定义为 Maybe<T> 的派生 classes,其中 Just<T> 是唯一具有T 类型的数据成员。这样做的问题是,然后 Monad<T>::bind 更好地实现为虚拟成员函数,或者至少那是我觉得最自然的东西,它不可能是因为它也是一个函数模板。

顺便说一句,我想知道是否有比

更简单的语法
template <typename Fun>
auto bind(Fun&& f) -> decltype(f(T{})) {
    typedef typename decltype(f(T{}))::value_type R;
    /*
     * blabla
     */
}

达到同样的效果,得到R.

在 C++ 中,编译器需要知道它正在处理的对象的大小,并且相同类型的对象具有相同的大小。恰好这个问题出现了,指针和多态就来了。

这意味着在您的情况下,您要么事先知道大小(并且承受 sizeof(T) 开销),要么回退到动态分配。后者可以通过多种方式完成:您可以使 Maybe<T> 多态,或者您可以分配 T 本身。

我不能忽视你代码中的一些问题;两者都与一个令人恼火的事实有关:类型 T 可能缺少默认构造函数。你可以忽略这个,或者你必须做一些修复。

首先,你不能做decltype(f(T{})),即使T{}只是为了类型推断,实际上并没有调用构造函数。有一种标准方法可以做到这一点:std::declval<T>() returns 一个 T 类型的对象(事实上,根本没有实现,只是声明;这对于类型推断来说已经足够了).所以,你应该做 decltype(f(std::declval<T>())).

其次,您不能简单地声明 T _value,因为在构造 Nothing<T> 时没有可调用的构造函数。这可以简单地通过动态分配来解决,但是还有另一种方法(在boost::optional中使用):存储一个char大小为sizeof(T)的数组,不初始化,然后使用[=42] =]在实际创建T类型的对象时放置新的。 here.

描述了这种方法

最后,回答你的最后一个问题:不,没有更简单的方法来做到这一点:

typedef typename decltype(f(T{}))::value_type R;

按照我之前的话,应该是这样的

typedef typename decltype(f(std::declval<T>()))::value_type R;