在下面的coliru中,您将找到我对“Maybe”monad的实现。
http://coliru.stacked-crooked.com/a/82978c254410ba6e
我遇到的问题是“Nothing”值带有一个不必要的T
类型的数据成员,就像需要它的“Just”值一样。
是否可以实现Maybe<T>
没有“Nothing”值与“Just”值一样大,并且不依赖于T
类型值的动态分配 ?
我尝试将Just<T>
和Nothing<T>
定义为Maybe<T>
的派生类,其中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
。
答案 0 :(得分:2)
在C ++中,编译器需要知道它正在使用的对象的大小,而相同类型的对象具有相同的大小。当这恰好是一个问题时,指针和多态就来了。
这意味着,在您的情况下,要么您知道前面的大小(并且有sizeof(T)
开销),要么您回退到动态分配。后者可以通过多种方式完成:您可以使Maybe<T>
多态,或者您可以自己分配T
。
我不能忽视你的代码的一些问题;两者都与一个令人恼火的事实有关:类型T
可能缺少默认构造函数。你可以忽略这一点,或者你必须做一些修复。
首先,你不能做decltype(f(T{}))
,即使T{}
仅用于类型推断而实际上没有调用构造函数。有一种标准方法可以做到这一点:std::declval<T>()
返回类型为T
的对象(实际上,根本没有实现,只是声明了;这对于类型推断就足够了)。所以,你应该做decltype(f(std::declval<T>()))
。
其次,您不能简单地声明T _value
,因为在构造Nothing<T>
时没有构造函数可以调用。这可以通过动态分配简单地解决,但还有另一种方法(在boost::optional
中使用):存储大小为char
的{{1}}数组,保持未初始化,并使用实际创建sizeof(T)
类型的对象时放置新。这种方法被描述为here。
最后,回答你的上一个问题:不,没有更简单的方法可以做到这一点:
T
根据我之前的说法,应该看起来像
typedef typename decltype(f(T{}))::value_type R;