原書(中文版)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源碼
這裡我簡要說一下,解決方法就是確保左端的直接父類是唯一的,這樣即可有一條唯一的路徑可以轉換到某一基類。
首先,給出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++設計新思維》一書朋友有用,其他博友慎讀(不要對C++產生抵觸情緒)