【问题标题】:Writing typeclasses in C++用 C++ 编写类型类
【发布时间】:2018-06-14 20:15:06
【问题描述】:

我正在尝试用 C++ 编写与一组类型类类似的东西,但我正在努力解决如何安排模板签名,或者是否可以做我想做的事情。

把它分解成最小的例子,假设我有这个:

template<typename S, typename T>
struct Homomorphism {
    //Defined in specialization: static const T morph(const S&);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};

template<typename S, typename T>
struct Monomorphism : Homomorphism<S, T> {
    //Defined in specialization: static const T morph(const &S);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};

我的程序中的数据类型有这些类(和其他态射)的特化。

我现在想做的是编写一个结构模板,它将采用两个同态或两个单态并将它们组合起来分别生成一个新的同态或单态结构,即:

template<typename S, typename T, typename U,
         typename HST = Homomorphism<S, T>,
         typename HTU = Homomorphism<T, U>,
         typename HSU = Homomorphism<S, U> >
struct CompositionMorphism : HSU {
    static const U morph(const S &s) {
        return HTU::morph(HST::morph(s));
    }
    static constexpr bool is_instance = true;
    using src  = S;
    using dest = U;
}

这实际上适用于通过以下方式组合同态的特殊实例:

CompositionMorphism<Class1, Class2, Class3>::morph(class1Instance);

当我有:

struct Homomorphism<Class1, Class2> {
    static const Class2 morph(const Class1 &c) {
        ...
    }
};

Homomorphism&lt;Class2, Class3&gt;类似。

不过,现在我想写:

template<typename S, typename T, typename U,
        typename MST = Monomorphism<S, T>,
        typename MTU = Monomorphism<T, U>,
        typename MSU = Monomorphism<S, U> >
struct CompositionMorphism : MSU {
    static const U morph(const S &s) {
        return MTU::morph(MST::morph(s));
    }
    static constexpr bool is_instance = true;
    using src  = S;
    using dest = U;
};

但毫无疑问,编译器会抱怨 CompositionMorphism 的重复定义。

有没有办法用HomomorphismMonomorphism 编写CompositionMorphism 及其专业化,这样我就可以做电话之类的事情:

template<> struct Homomorphism<Class1, Class2> { ... };
template<> struct Homomorphism<Class2, Class3> { ... };
CompositionMorphism<Class1, Class2, Class3>::morph(c1Instance);

或:

template<> struct Monomorphism<Class1, Class2> { ... };
template<> struct Monomorphism<Class2, Class3> { ... };
CompositionMorphism<Class1, Class2, Class3>::morph(c1Instance);

或:

template<> struct Monomorphism<Class1, Class2> { ... };
template<> struct Homomorphism<Class2, Class3> { ... };
CompositionMorphism<Class1, Class2, Class3>::morph(c1Instance);

并让编译器根据我的态射层次选择最接近的CompositionMorphism 特化?

【问题讨论】:

  • CompositionMorphism(Class1, Class2, Classe) — 你的意思是 Class3 和尖括号吗?
  • 我实际上并没有得到,什么时候应该选择任何一个类。 (在什么条件下)
  • CompositionMorphism 如何知道它应该使用Homomorphism 还是Monomorphism?您需要消除歧义,然后一切都很好。由于CompositionMorphism 可以同时创建HomoMono 的新版本,因此您不能依赖以前的显式实例化来进行此选择。
  • @yeputons 是的,没错。感谢您指出。我会解决的。
  • @super 我最终想要实现的技巧是能够对特殊情况进行特化(即产生比普通同态更特殊的态射形式的态射组合,例如结合两个单态),并让它们默认为组成同态的“基本”模板,如果它们不符合特殊组合之一。

标签: c++ templates metaprogramming typeclass template-meta-programming


【解决方案1】:

您可以尝试编写一个模板,以根据 morph 函数上的 SFINAE 选择 HomomorphismMonomorphism

template <typename S, typename T, typename = void>
struct SelectMorphism {
    using type = Homomorphism<S, T>;
};

template <typename S, typename T>
struct SelectMorphism<S, T, std::enable_if_t<std::is_same_v<decltype(Monomorphism<S, T>::morph(std::declval<S>())), const T>>> {
    using type = Monomorphism<S, T>;
};

这将检查Monomorphism&lt;S, T&gt;::morph(S) 是否会返回T,如果是则选择Monomorphism&lt;S, T&gt;。如果不是,SFINAE 将失败并默认为 Homomorphism&lt;S, T&gt;

然后我们把CompositionMorphism改成这样使用这个模板

template<typename S, typename T, typename U,
         typename HST = typename SelectMorphism<S, T>::type,
         typename HTU = typename SelectMorphism<T, U>::type,
         typename HSU = typename SelectMorphism<S, U>::type >
struct CompositionMorphism : HSU {
    static const U morph(const S &s) {
        return HTU::morph(HST::morph(s));
    }
    static constexpr bool is_instance = true;
    using src  = S;
    using dest = U;
};

您可以看到这个完整工作示例的live demo here。它需要c++17,但也可以写成c++11(稍微冗长一些)。

#include <iostream>

template<typename S, typename T>
struct Homomorphism {
    //Defined in specialization: static const T morph(const S&);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};

template<typename S, typename T>
struct Monomorphism : Homomorphism<S, T> {
    //Defined in specialization: static const T morph(const &S);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};

template <typename S, typename T, typename = void>
struct SelectMorphism {
    using type = Homomorphism<S, T>;
};

template <typename S, typename T>
struct SelectMorphism<S, T, std::enable_if_t<std::is_same_v<decltype(Monomorphism<S, T>::morph(std::declval<S>())), const T>>> {
    using type = Monomorphism<S, T>;
};

struct Class1 {};

struct Class2 {};

struct Class3 {};

template<>
struct Monomorphism<Class1, Class2> : Homomorphism<Class1, Class2> {
    static const Class2 morph(const Class1&) { std::cout << "Morphing in Mono<Class1, Class2>" << std::endl; return Class2{}; }
    static constexpr bool is_instance = false;
    using src  = Class1;
    using dest = Class2;
};

template<>
struct Homomorphism<Class2, Class3> {
    static const Class3 morph(const Class2&) { std::cout << "Morphing in Homo<Class2, Class3>" << std::endl; return Class3{}; }
    static constexpr bool is_instance = false;
    using src  = Class2;
    using dest = Class3;
};

template<typename S, typename T, typename U,
         typename HST = typename SelectMorphism<S, T>::type,
         typename HTU = typename SelectMorphism<T, U>::type,
         typename HSU = typename SelectMorphism<S, U>::type >
struct CompositionMorphism : HSU {
    static const U morph(const S &s) {
        return HTU::morph(HST::morph(s));
    }
    static constexpr bool is_instance = true;
    using src  = S;
    using dest = U;
};

int main ()
{
    CompositionMorphism<Class1, Class2, Class3>::morph(Class1{});
}

【讨论】:

  • 这太棒了(在这一点上,我很头疼,所以今晚我将浏览 STL 页面),但我还有几个问题。首先,示例中的 HSU 类型是什么?我不知道,但我希望能够推断出合成产生的态射。其次,如果我在混合中再加入两个态射,这将如何工作:比如说一个 Epimorphism,它是 Homomorphism 的子类,以及 Isomorphism,它是 Epimorphism 和 Monomorphism 的子类?这甚至可以捕获吗?
  • 哦,C++17 是理想的选择,因为我正在尝试尽可能多地探索 C++17 的开发。
  • @Sebastian HSU 在这个例子中是Homomorphism&lt;Class1, Class3&gt;。由于Mono&lt;C1, C3&gt; 没有专门化,它默认为Homo。然而,Homo 也没有专门化,因此Homomorphism 的非专门化版本被实例化。它没有morph 函数,但它仍然有效,因为该类从未调用过morph
  • @Sebastian 这个例子绝对可以扩展到包含更多态射。如果有一个简单的规则要遵循,应该选择哪个或按什么顺序选择它们,那么扩展它可能是可以的。如果选择哪种类型的规则更复杂,这种自动选择模板可能会变得非常混乱且难以编写/维护/调试。
【解决方案2】:

正如 Super 所观察到的,如果您仅传递 TUV,编译器不知道是选择 Homomorphism 还是 Monomorphism

所以我想你应该通过Homomorphism&lt;T, U&gt;Homomorphism&lt;U, V&gt;(可以构造Homomorphism&lt;T, V&gt;)或Monomorphism&lt;T, U&gt;Monomorphism&lt;U, V&gt;

如果你想强加两个Homomorphism两个Monomorphism(我的意思是:如果你想排除MonomorphismHomomorphism)你可以写如下内容

template <typename, typename>
struct CompositionMorphism;

template <template <typename, typename> class C,
          typename S, typename T, typename U>
struct CompositionMorphism<C<S, T>, C<T, U>>
 {
   using comp = C<S, U>;

   static const U morph (const S & s)
    { return C<T, U>::morph(C<S, T>::morph(s)); }
 };

并按如下方式调用它

   Homomorphism<int, long>        h0;
   Homomorphism<long, long long>  h1;
   Monomorphism<int, long>        m0;
   Monomorphism<long, long long>  m1;

   CompositionMorphism<decltype(h0), decltype(h1)>  h2;
   CompositionMorphism<decltype(m0), decltype(m1)>  m2;

   // compiler error
   //CompositionMorphism<decltype(h0), decltype(m1)>  hm;

以下是完整的编译示例

#include <array>
#include <iostream>

template <typename S, typename T>
struct Homomorphism
 {
   //Defined in specialization: static const T morph(const S&);
   static constexpr bool is_instance = false;
   using src  = S;
   using dest = T;
 };

template <typename S, typename T>
struct Monomorphism : Homomorphism<S, T>
 {
   //Defined in specialization: static const T morph(const &S);
   static constexpr bool is_instance = false;
   using src  = S;
   using dest = T;
 };

template <typename, typename>
struct CompositionMorphism;

template <template <typename, typename> class C,
          typename S, typename T, typename U>
struct CompositionMorphism<C<S, T>, C<T, U>>
 {
   using comp = C<S, U>;

   static const U morph (const S & s)
    { return C<T, U>::morph(C<S, T>::morph(s)); }
 };


int main ()
 { 
   Homomorphism<int, long>        h0;
   Homomorphism<long, long long>  h1;
   Monomorphism<int, long>        m0;
   Monomorphism<long, long long>  m1;

   CompositionMorphism<decltype(h0), decltype(h1)>  h2;
   CompositionMorphism<decltype(m0), decltype(m1)>  m2;

   // compiler error
   //CompositionMorphism<decltype(h0), decltype(m1)>  hm;

   static_assert( std::is_same<Homomorphism<int, long long>,
                               decltype(h2)::comp>{}, "!" );

   static_assert( std::is_same<Monomorphism<int, long long>,
                               decltype(m2)::comp>{}, "!" );
 }

【讨论】:

  • 实际上,我希望能够将每种态射类型(有六种,有些具有多重继承)与其他所有态射类型包括在内:但是,只有六种特殊情况,并且适用于所有情况否则,我希望它能够默认回到与态射的父类有关的最接近的情况。 (Homo / homo 是最通用的。)我很快就会看一下这一切。谢谢!
  • 一个函数 make_xx 可能会有所帮助:template &lt;typename M1, typename M2&gt; CompositionMorphism&lt;M1, M2&gt; ComposeMorphism(const M1&amp;, const M2&amp;) { return {}; } 然后是 auto h2 = ComposeMorphism(h0, h1);
  • 而且,事实上我认为 OP 只是想要 ComposeMorphism 的不同重载。
  • 就是这样。我希望能够使用不同类型的态射作为参数重载ComposeMorphism,但我似乎无法实现。
【解决方案3】:

好的,有时我需要更多的思考,但这可能是您正在寻找的:

#include <type_traits>
#include <cstdint>
#include <tuple>

template<typename S, typename T>
struct Homomorphism;

template<typename S, typename T>
struct Monomorphism;

class Class1{};
class Class2{};
class Class3{};

template<> struct Homomorphism<Class1, Class2> 
{ 
     static const Class2 morph(const Class1&); 
     static constexpr bool is_instance = true;#
};

template<> struct Homomorphism<Class2, Class3> 
{
    static const Class3 morph(const Class2&);
    static constexpr bool is_instance = true;
};

template<typename S, typename T>
struct Homomorphism {
    //Defined in specialization: static const T morph(const S&);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};

template<typename S, typename T>
struct Monomorphism : Homomorphism<S, T> {
    //Defined in specialization: static const T morph(const &S);
    static constexpr bool is_instance = false;
    using src  = S;
    using dest = T;
};


namespace details {
    template<typename T, typename U, std::enable_if_t<Homomorphism<T,U>::is_instance>* = nullptr>
    U morph (const T& t)
    {return  Homomorphism<T,U>::morph(t);}

    template<typename T, typename U,  std::enable_if_t<Monomorphism<T,U>::is_instance>* = nullptr>
    U morph (const T& t)
    {return  Monomorphism<T,U>::morph(t);}


 }

template <typename S, typename T, typename U>
class CompositionMorphism
{
public:
    static U morph (const S& s)  {return  details::morph<T,U>(details::morph<S,T>(s));}
    static constexpr bool is_instance = true;
};


 int main(int, char**)
{
    Class1 c1Instance;
    CompositionMorphism<Class1, Class2, Class3>::morph(c1Instance);
    std::ignore = d;
}

您可能希望手动创建组合的同/单态射,如下所示:

template <> class Monomorphism<Class1,Class3> : public CompositionMorphism<Class1, Class2, Class3> {};

然后它们可以被 CompositionMorphism 自动重用。

【讨论】:

  • 我认为这会让我到达我想去的地方,因为它允许我任意混合不同的态射类型。
  • 关于手动创建 composes Homo/Mono 态射的最后一行我还有一个问题(这是我想要做的。
  • 基本上,我有一个查找表,基于态射类型对,可以确定组合态射的类型。例如,由外同构组成的单态是同态,而由自同构组成的单态是单态。有什么方法可以对此进行编码并在最后一行手动创建的示例中使用此类型信息?
  • 如果不清楚,代替模板 class Monomorphism,有什么办法可以查询的态射类型, Class3> 并使用它来分配类型,而不是作为用户需要知道这是一个单态?
猜你喜欢
  • 2016-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-05
  • 2010-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多