我正在使用C++ 11,並試圖在我的應用程序中設置一個通用的Handle
類,其中有時可能使用不同的基礎類型轉換句柄,但僅限於如果底層類型與祖先/後代相關,否則嘗試進行轉換應該簡單地失敗。我還需要一個永不失敗的函數,告訴我兩種類型之間是否可以進行轉換。特別是,我不希望基礎類型嘗試對不在其自己的祖先/後代行中的類型進行任何轉換,所以我想如果我在編譯時告訴我的布爾值定義了模板仿函數,類型是相關的,並且使用模板專業化來拒絕轉換(如果它們不相關),或者如果轉換請求相關,則將轉換請求轉發給基礎類型。每個基類都包含一個模板化轉換函數,該函數知道如何轉換爲其中的每個相應類型以及模板布爾函數,該函數根據類實例的內部狀態指示是否可以進行此類轉換。在編譯時選擇使用模板調用哪個函數
我放在一起看起來是這樣的:
template<class T>
class MyHandle {
public:
...
template<bool> struct can_be_ref {
template<class U> bool operator()(const MyHandle *, const U*) const
{
}
};
template<bool> struct as_ref {
template<class U> MyHandle<U> operator()(const MyHandle *, const U*) const
{
throw std::runtime_error("Illegal type conversion");
}
};
template<class U> bool can_be();
template<class U> MyHandle<U> as();
private:
const T* get_member_reference() const;
};
template<class T> struct MyHandle<T>::can_be_ref<true> {
template<class U> bool operator()(const MyHandle<T> *ptr, const U*)
{
ptr->get_member_reference()->can_be<U>();
}
};
template<class T> struct MyHandle<T>::as_ref<true> {
template<class U> MyHandle<U> operator()(const MyHandle<T> *ptr, const U*) const
{
return ptr->get_member_reference()->as<U>();
}
};
template<class T> template<class U> bool MyHandle<T>::can_be()
{
return can_be_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr));
}
template<class T> template<class U> MyHandle<U> MyHandle<T>::as()
{
return as_ref < std::is_base_of<T, U>::value || std::is_base_of<U, T>::value >()(this, reinterpret_cast<const U *> (nullptr));
}
這並不編譯,但是,我不知道我做錯了。失敗發生在我嘗試專門化can_be_ref
和as_ref
結構體,其中編譯器抱怨太少的模板參數列表。
希望我想要做的事情在我提供的解釋和遺憾的代碼片段之間清楚無效,但它是我能想到的唯一方法來描述我想要做的事情。我究竟做錯了什麼?
編輯:
澄清,說我有下面的類層次結構:
class A {
public:
template<class U> bool can_be();
template<class U> MyHandle<U> as();
...
};
class B : public A{
...
};
class C {
public:
template<class U> bool can_be();
template<class U> MyHandle<U> as();
...
};
每個層級定義了一個can_be
和as
方法只關注如何在其層次結構中的項目,並在如果模板的參數不是正確的類型,在某些情況下可能會導致編譯器錯誤,這就是編譯時必須檢查該類型的原因。
而假設我們有以下變量定義:
MyHandle<A> a;
MyHandle<B> b;
MyHandle<C> c;
因爲a
和b
是相關類型的,A::can_be
和A::as
它們之間可以自由使用,但是A :: can_be可能會產生一個編譯錯誤。因此,MyHandle中的包裝器將其隱藏起來,例如MyHandle<A>::can_be<C>()
就會返回false。雖然MyHandle<B>::as<C>()
總是會引發異常,甚至不會嘗試生成對B::as<C>
的調用,因爲這可能會導致編譯錯誤。
編輯:下面
每卡米爾的建議,解決辦法是migate模板定義到周圍的類。我所做的就是創建一個輔助模板如下:
template<class T,class U,bool> class MyHandleConverter
{
public:
inline MyHandleConverter(const MyHandle<T> *) { }
inline bool can_be() const { return false; }
inline MyHandle<U> as() const { return MyHandle<U>(nullptr); }
};
我決定放棄投擲無效轉換例外,現在MyHandle的每個實例包含一個名爲value
一個空指針,可以包含一個指向更多信息實際的基礎類型,它是nullptr如果它是無效的,所以我可以再創建爲MyHandleConverterClass部分專業化如下:
template<class T,class U> class MyHandleConverter<T,U,true> {
public:
inline MyHandleConverter(const MyHandle<T> *ref):reference(ref) { }
inline bool can_be() const {
if (std::is_base_of<T,U>::value) {
return true;
} else if (reference->value == nullptr) {
return false;
} else {
return reference->underlying_can_be((const U*)(nullptr));
}
}
inline MyHandle<U> as() const {
if (std::is_base_of<U,T>::value) {
return MyHandle<U>(reference->value);
} else if (reference->value == nullptr) {
return MyHandle<U>(nullptr);
} else {
return reference->underlying_as((const U*)(nullptr));
}
}
private:
const MyHandle<T> *reference;
};
而是像我一樣以前拋出異常的,我不是返回一個無效MyHandle (它有一個特殊的構造函數,MyHandle(nullptr_t)
和MyHandle
的狀態可以通過一個簡單的布爾值is_valid()
方法來查詢(如果需要,調用者可以選擇拋出一個異常,這對於我的目的而言,不得不編寫更少的try .... catch塊比如果我有as<U>
函數本身在失敗時拋出異常)。
的MyHandle類有一個模板underlying_can_be
方法和模板underlying_as
方法,該方法簡單地轉發它們分別請求到下面的類類型的can_be
as
或方法。值得一提的是,這些方法甚至不會編譯器生成的,如果它沒有被通過MyHandleConverter<T,U,true>
類調用,所以現在的MyHandle can_be
和as
方法是這樣寫的:
template <class T> template<class U> bool MyHandle<T>::can_be() const {
return MyHandleConverter<T, U, are_related_handle_types<U,T>()>(this).can_be();
}
template<class T> template<class U> MyHandle<U> MyHandle<T>::as() const {
return MyHandleConverter<T, U, are_handle_types_related<U,T>()>(this).as();
}
其中are_handle_types_related
是一個模板constexpr函數,如果調用底層模板類型時發現該函數關係緊密,那麼調用MyHandle的can_be
或has
方法的底層類型不會導致編譯器錯誤,或者在某些情況下會導致無法在編譯時檢測到的邏輯錯誤時間,甚至在運行時不需要在每個基礎類型中編寫複雜的檢測邏輯和can_be
方法,只需通過檢測這兩個類是從適當的類型派生出來,以便轉換過程合理地成功。
這樣一來,當類型不兼容的are_handle_types_related
檢測到,這將是無效的調用相應類型的can_be
或as
方法,所創建的MyHandleConverter
實例爲MyHandleConverter<T,U,false>
不嘗試調用底層類類型,而MyHandleConverter<T,U,true>
確實,但只會實例化已經發現可以接受調用底層類型的適當轉換函數的類。
您希望如何使用他們目前尚不清楚給我。什麼是一些有效的用法?什麼是一些無效的用法?發佈它們將非常有用。 –