应该有两个操作:mempty是一个中性元素,而mappend是一个关联二元操作。 我无法决定什么是设计monoid的正确方法。我可以使用模板专业化
template<typename T>
T mappend(const T &left_operand, const T &right_operand);
template<typename T>
T mempty();
struct Min {
int value;
};
template<>
Min mappend<Min>(const Min &left_operand, const Min &right_operand) {
return {std::min(left_operand.value, right_operand.value)};
}
template<>
Min mempty<Min>() {
return {std::numeric_limits<int>::max()};
}
int main() {
Min m1{1};
Min m2{3};
std::cout << mappend(mempty<Min>(), mappend(m1, m2)).value << std::endl;
return 0;
}
然而,通过这种方式,我无法确保mappend和mempty都专门用于该课程。
另一种方法是创建模板类
template<typename ValueT, ValueT Identity, typename AppendT, AppendT Append>
class Monoid {
static constexpr ValueT EMPTY = Identity;
static constexpr AppendT APPEND = Append;
ValueT value = Identity;
public:
using monoid = Monoid<ValueT, Identity, AppendT, Append>;
constexpr Monoid() noexcept = default;
constexpr explicit Monoid(const ValueT &value) noexcept : value(value) {};
constexpr ValueT getValue() const noexcept {
return value;
}
template<typename V, V I, typename AT, AT A>
friend constexpr monoid
mappend(const monoid &left_operand, const monoid &right_operand) {
return {APPEND(left_operand.value, right_operand.value)};
}
template<typename V, V I, typename AT, AT A>
friend constexpr monoid
mempty() {
return EMPTY;
}
};
int main() {
auto add = [](const int &x1, const int &x2) { return x1 + x2; };
using Sum = Monoid<int, 0, decltype(add), add>;
Sum m1(2);
Sum m2(3);
std::cout << Sum::mappend(Sum::mappend(m1, m2), Sum::mempty()).getValue() << std::endl;
}
由于我无法将lambda作为模板参数传递,因此无法编译。
我想知道是否有更好的设计或我应该如何解决我的问题
答案 0 :(得分:3)
如果您可以将Append
的类型限制为概念DefaultConstructible
,则不必使用一个实例来声明Monoid
。像std::plus<>
这样的类型很适合作为Monoid
template <typename Arithmetic>
using Sum = Monoid<Arithmetic, 0, std::plus<Arithmetic>>;
template <typename Arithmetic>
using Product = Monoid<Arithmetic, 1, std::multiplies<Arithmetic>>;