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;
在下面的 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;