• 《C++设计新思维》勘误,附C++14新解法


    勘误:

    原书(中文版)3.13节,65-69页中GenScatterHierarchy以及FieldHelper均存在问题,当TypeList中类型有重复时,无法通过编译(原因在于“二义性基类”)。

    书中出现的二义性问题,可以用一小段代码演示一下:

    class A{};
    class B:public A{};
    class C:public A,public B{};
    
    void test()
    {
        C c;
        A& cf =c;//wrong,don't try this at home.  
        B& cbf =c;//right
        A& caf = cbf;//right
    }

    由于C继承了两个A,一个直接继承,一个间接继承,所以将C转换成A,存在两条路径,无法转换。甚至我们永远无法访问C直接继承的A!

    继承B的路径是唯一的,所以可以通过B,再次转换成为A。

    《C++设计新思维》书中给出的代码和类图,向右侧转换是唯一的,但是向左侧转换时路径不为一,所以TypeList一旦包含重复类型后,无法通过编译。

    这个问题已经解决了,具体解决方案请参看Loki源码 Loki源码

    这里我简要说一下,解决方法就是确保左端的直接父类是唯一的,这样即可有一条唯一的路径可以转换到某一基类。

    C++14 新法:

    首先,给出typelist和操作typelist的两个函数

    template <typename... T> struct TypeList;
    
    template <int I, typename Arg>
    struct at;
    template <int I, typename Head, typename... Tail>
    struct at<I, TypeList<Head, Tail...>>
    {
        typedef typename at<I - 1, TypeList<Tail...>>::type type;
    };
    template <class Head, typename... Tail>
    struct at<0, TypeList<Head, Tail...>>
    {
        typedef Head type;
    };
    template <int N, class Seq>
    struct drop;
    template <int N, class Head, class... Tail>
    struct drop<N, TypeList<Head, Tail...>>
    {
        typedef
        typename drop<N-1, TypeList<Tail...>>::type
        type;
    };
    template <class Head, class... Tail>
    struct drop<0, TypeList<Head, Tail...>>
    {
        typedef TypeList<Head, Tail...> type;
    };

    at获取typelist中某个索引值的类型元素,而drop是去除首部的某几个元素。

    下面给出GenScatterHierarchy代码,此处为了方便使用Genorator代之。

    namespace Private
    {
    // The following type helps to overcome subtle flaw in the original
    // implementation of GenScatterHierarchy.
    // The flaw is revealed when the input type list of GenScatterHierarchy
    // contains more then one element of the same type (e.g. LOKI_TYPELIST_2(int, int)).
    // In this case GenScatterHierarchy will contain multiple bases of the same
    // type and some of them will not be reachable (per 10.3).
    // For example before the fix the first element of Tuple<LOKI_TYPELIST_2(int, int)>
    // is not reachable in any way!
    template<class, class>
    struct UniqueTag;
    }
    
    
    
    template <typename Head,typename... Tails, template <typename> class Unit> class Genorater<TypeList<Head,Tails...>,Unit>
            :public Genorater<Private::UniqueTag<Head,TypeList<Head,Tails...>>,Unit>,public Genorater<TypeList<Tails...>,Unit> {};
    template <typename Head,typename... Tails,template <typename> class Unit> class Genorater<Private::UniqueTag<Head,TypeList<Head,Tails...>>,Unit>:public Unit<Head> {};

    UniqueTag,用于辅助构建唯一的类型转换路径。

    下面给出FieldHelper源码:

    template <int I,typename... TList,template <typename> class Unit> Unit<typename at<I,TypeList<TList...>>::type>& FieldHelper(Genorater<TypeList<TList...>,Unit>& obj)
    {
     
        Genorater<Private::UniqueTag<typename at<I,TypeList<TList...>>::type,typename drop<I,TypeList<TList...>>::type>,Unit>& leftBase = obj;
        return leftBase;
     
    }
     
    template <int I,typename... TList, template <typename> class Unit> Unit<typename at<I,TypeList<TList...>>::type>& Field(Genorater<TypeList<TList...>,Unit>& obj)
    {
     
        //return FieldHelper(obj,Int2Type<I>());
        return FieldHelper<I>(obj);
    }
     

    最后是测试代码:

    typedef TypeList<double,int,double,string> myList;
    
    typedef Genorater<myList,Holder> Info;
    
    int main()
    {
        cout << "Hello world!" << endl;
      
        Info obj;
        cout<<typeid(obj).name()<<endl;
        Field<1>(obj).value_=1;
        Field<2>(obj).value_=2;
        Field<3>(obj).value_="hao123";
        cout<< Field<1>(obj).value_<<endl;
        cout<< Field<2>(obj).value_<<endl;
        cout<< Field<3>(obj).value_<<endl;
     
     
        return 0;
    }

    -------------------------------------------------------------------------华丽的分割线---------------------------------------------------------------------------------------

    此处给出一个仿写Loki源码的FieldHelper

    namespace Private
    {
    template<class, class...>
    struct UniqueTag;
    }
    
    template <typename Head,typename... Tails, template <typename> class Unit> class Genorater<TypeList<Head,Tails...>,Unit>
            :public Genorater<Private::UniqueTag<Head,Tails...>,Unit>,public Genorater<TypeList<Tails...>,Unit> {};
    template <typename Head,typename... Tails,template <typename> class Unit> class Genorater<Private::UniqueTag<Head,Tails...>,Unit>:public Unit<Head> {};
    
    
    template <typename Head,typename... TList,template <typename> class Unit> Unit<Head>& FieldHelper(Genorater<TypeList<Head,TList...>,Unit>& obj,Int2Type<0>){
    
                  Genorater<Private::UniqueTag<Head,TList...>,Unit>& leftBase =  obj;
                  return leftBase;
    }
    template <int I,typename Head,typename... TList, template <typename> class Unit> Unit<typename at<I,TypeList<Head,TList...>>::type>& FieldHelper(Genorater<TypeList<Head,TList...>,Unit>& obj,Int2Type<I>){
    
    Genorater <TypeList<TList...>,Unit>& rightBase = obj;
    return FieldHelper(rightBase,Int2Type<I-1>());
    }

    补充一些内容,其实C++11可利用变模板参数而剔除typelist。比如C++11标准的tuple的实现并没有利用typelist技术。

    tuple利用了多重继承,实现方法和Genorator方式大同小异。C++11采用左端采用公有继承,右端使用私有继承。并采用int类型模板参数,作为转换路径唯一的标示。

    具体见C++11 tuple.h中 相关源码。

    接下来我不使用typelist技术来实现单根继承

    class empty{};
    
    template <template <typename,typename> class Unit,typename Head,typename... Tail> class GenorateLiner
    :public Unit<Head,GenorateLiner<Unit,Tail...>>{
        
    };
    
    template <template <typename,typename> class Unit,typename Head> class GenorateLiner<Unit,Head>:Unit<Head,empty>{
    
    };

    谢谢,转载请表明出处!本文仅对读过《C++设计新思维》一书朋友有用,其他博友慎读(不要对C++产生抵触情绪)

  • 相关阅读:
    2. redis的数据类型
    1. redis简介
    6. 职责链设计模式
    9. 桥梁设计模式
    一. 序言
    网络中常见的面试题
    JDBC
    Mybatis批量操作数据的方法
    ORM框架的概述
    Mybatis动态sql技术
  • 原文地址:https://www.cnblogs.com/tangzhenqiang/p/4270609.html
Copyright © 2020-2023  润新知