2017-04-08 76 views
2

我正在使用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_refas_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_beas方法只關注如何在其層次結構中的項目,並在如果模板的參數不是正確的類型,在某些情況下可能會導致編譯器錯誤,這就是編譯時必須檢查該類型的原因。
而假設我們有以下變量定義:

MyHandle<A> a; 
MyHandle<B> b; 
MyHandle<C> c; 

因爲ab是相關類型的,A::can_beA::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_beas或方法。值得一提的是,這些方法甚至不會編譯器生成的,如果它沒有被通過MyHandleConverter<T,U,true>類調​​用,所以現在的MyHandle can_beas方法是這樣寫的:

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_behas方法的底層類型不會導致編譯器錯誤,或者在某些情況下會導致無法在編譯時檢測到的邏輯錯誤時間,甚至在運行時不需要在每個基礎類型中編寫複雜的檢測邏輯和can_be方法,只需通過檢測這兩個類是從適當的類型派生出來,以便轉換過程合理地成功。

這樣一來,當類型不兼容的are_handle_types_related檢測到,這將是無效的調用相應類型的can_beas方法,所創建的MyHandleConverter實例爲MyHandleConverter<T,U,false>不嘗試調用底層類類型,而MyHandleConverter<T,U,true>確實,但只會實例化已經發現可以接受調用底層類型的適當轉換函數的類。

+0

您希望如何使用他們目前尚不清楚給我。什麼是一些有效的用法?什麼是一些無效的用法?發佈它們將非常有用。 –

回答

2

專注你有專業化前增加template關鍵字模板,如:

template<class T> // Template parameter for 'MyHandle<T>' 
template<> // No unspecialized template parameters for 'can_be_ref', but indicate that it is a template anyway 
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>(); 
    } 
}; 

然而,這並不編譯任。根據http://en.cppreference.com/w/cpp/language/template_specialization

成員或成員模板可嵌套在許多封閉類 模板中。對於這樣一個成員的顯式專門化,對於每個專門顯式爲 的封閉類模板,都有一個 模板<>。在這樣一個嵌套的聲明,一些水平可能 保持非特

因此,我們不能完全專注模板沒有(不同之處在於,如果它包圍類是非特不能專注一類成員 模板)專門研究MyHandle。解決方案可能是模板參數偏特 - 移動參數Ucan_be_ref::operator()can_be_ref級別:

template<class T> 
class MyHandle 
{ 
public: 
... 
    template<class U, bool> 
    struct can_be_ref 
    { 
     bool operator()(const MyHandle<T> *ptr, const U*) const 
     { 
      return false; 
     } 
    }; 

    template<class U, bool> 
    struct as_ref 
    { 
     MyHandle<U> operator()(const MyHandle<T> *, const U*) const 
     { 
      throw std::runtime_error("Illegal type conversion"); 
     } 
    }; 
... 
}; 

然後我們可以去一個偏特:

template<class T> 
template<class U> 
struct MyHandle<T>::can_be_ref<U, true> 
{ 
    bool operator()(const MyHandle<T> * ptr, const U*) const 
    { 
     return ptr->get_member_reference()->can_be<U>(); 
    } 
}; 

template<class T> 
template<class U> 
struct MyHandle<T>::as_ref<U, true> 
{ 
    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() const 
{ 
    return can_be_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

template<class T> 
template<class U> MyHandle<U> MyHandle<T>::as() 
{ 
    return as_ref<U, 
      std::is_base_of<T, U>::value || std::is_base_of<U, T>::value>()(
      this, nullptr); 
} 

其實,當我遵守它例如類AB,C遵從投訴在線return ptr->get_member_reference()->can_be<U>();即:expected primary-expression before ')' token。我真的不知道這裏有什麼問題。稱它爲get_member_reference()->A::can_be<U>()工作。得到成功的解決方法是通過傳遞U類型的參數來確定can_be<U>()U參數:

class A { 
public: 
    template<class U> bool can_be(const U*) 
{ 
return can_be<U>(); 
} 
    template<class U> MyHandle<U> as(const U*) 
{ 
return as<U>(); 
} 

    template<class U> bool can_be(); 
    template<class U> MyHandle<U> as(); 
}; 

template<class T> 
    template<class U> 
    struct MyHandle<T>::can_be_ref<U, true> 
    { 
     bool operator()(const MyHandle<T> * ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->can_be(uptr); 
     } 
    }; 

    template<class T> 
    template<class U> 
    struct MyHandle<T>::as_ref<U, true> 
    { 
     MyHandle<U> operator()(const MyHandle<T> *ptr, const U* uptr) const 
     { 
      return ptr->get_member_reference()->as(uptr); 
     } 
    };