В следующем coliru вы найдете мою реализацию монады «Возможно».
http://coliru.stacked-crooked.com/a/82978c254410ba6e
Проблема, которую я имею с этим, состоит в том, что ценности «Ничего» несут с собой ненужное 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
,
В 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
размера sizeof(T)
, оставьте его неинициализированным и используйте размещение нового при создании объекта типа T
, Этот подход описан Вот.
Наконец, отвечая на ваш последний вопрос: нет, не существует более простого способа сделать это:
typedef typename decltype(f(T{}))::value_type R;
который, по моим предыдущим словам, должен выглядеть
typedef typename decltype(f(std::declval<T>()))::value_type R;
Других решений пока нет …