我该如何设计幺半群类型

时间:2018-02-15 10:55:39

标签: c++ templates

应该有两个操作: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作为模板参数传递,因此无法编译。

我想知道是否有更好的设计或我应该如何解决我的问题

1 个答案:

答案 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>>;