2013-12-08 64 views
0

簡化我有下面的類層次結構:避免模板轉換參數

template<typename T> 
double foo_template(void* p) { 
    Vec<T>* v = reinterpret_cast<Vec<T>*>(p); 
    // use v which involves calling get 
    return result; 
} 

double foo(void* p, int type) { 
    if (type == 0) { 
    return foo_template<double>(p); 
    } else if (type == 1) { 
    return foo_template<int>(p); 
    } else if (...) { 
    // etc. 
    } else { 
    //unsupported type 
    } 
} 

(我可以用一個開關:

class BaseVec { 
    public: 
    BaseVec() {}; 
    virtual ~BaseVec() {}; 

    virtual double get_double(int i) const = 0; 
}; 

template<typename T> 
class Vec : public BaseVec { 
    public: 
    Vec() { ... }; 
    ~Vec() { ... }; 

    T get(int i) const { ... }; 

    double get_double(int i) const { 
     return get(i); 
    }; 
}; 

在項目中,我多次與以下形式的代碼結束並使用枚舉,或先投pBaseVec,然後做dynamic_cast s,但邏輯保持不變)

這不是主意l保持。例如,當我添加一個我想支持的附加類時,我必須爲每個if-else-if塊添加一個子句。

簡化此操作的一種可能方法是將p轉換爲BaseVec*並使用get_double方法。然而,由於這種方法經常被調用,這導致性能較差。此外,這並不總是可能的:有時我想打電話給get方法,因爲返回的類型很重要。

我嘗試使用訪問者模式,雖然這有一些優點,但它仍然意味着我必須爲每個可能的模板參數編寫一段代碼。

是否有某種方法可以使此代碼更容易維護?

PS:我沒有(很多)控制foo的內容。 foo被外部程序調用(R是確切的)。因此,我只能傳遞通用指針,int,double和character vectors到foo

PPS:建議更好的標題也是受歡迎的。

回答

0

爲什麼不改變foo_templatè是:

template<typename T> 
double foo_template(Vec<T>*) { 
    // use v which involves calling get 
    return result; 
} 

foo是:

template<typename T> 
double foo (Vec<T>* v) 
{ 
return foo_template(v) 
} 

,讓參數推導做的工作?

(你或許可以擺脫的功能之一,但我想保持儘可能接近原)

+0

感謝。那會更好。但是,正如我在PS中所說的,我對於傳遞給'foo'的東西幾乎沒有影響。這段代碼最終會在一個被另一個程序調用的庫中(R是確切的)。這個程序不知道'Vec's,所以我得到的是一些泛型指針,我必須將它們轉換爲正確的類型。 –

+0

更新了問題以反映之前的評論。 –

0

在C++自動調度通過VIRTUA功能的手段,並與static_type多態性與運行時多態性發生由static_Cast的mnas,但你需要知道什麼類型投。

隨着不同的設計,避免了void*,你可以做到以下幾點:

template<class Derived> 
class static_polimorphic {}; 

template<class A> 
A& upcast(static_polymorphic<A>& sa) 
{ return static_cast<A&>(sa); } 

template<class A> 
const A& upcast(const static_polymorphic<A>& sa) 
{ return static_cast<const A&>(sa); } 

現在,你的類應和像

class C1: public static_polymorphic<C1> 
{ 
.... 
}; 

class C2: public static_polymorphic<C2> 
{ 
.... 
}; 

則多態性將適用

template<class A> 
void function(const static_polymorphic<A>& sa) 
{ 
    A& a = upcast(sa); 
    a.methods(); 
    ... 
} 

換句話說,類型不再是一個基本成員變量,而是一個基本模板參數。

還要注意,作爲派生類型區分的基礎,常用函數不會被認爲是虛擬的。您可以完全避免基於運行時的多態性,除非您必須將不同的運行時類型創建的對象存儲到同一個容器或集合中。

爲此,您可以在派生類中使用第二個非抽菸虛擬函數作爲「啓動器」。 (可能更好使用運行時多態一作爲第一個基地,以簡化運行時間指針轉換,因爲將不會有偏移量)

+0

謝謝。也許我並不完全理解你的例子做了什麼,但據我所知,這仍然不能避免我擁有的'switch'或'if ... elseif'語句。當調用'function'時,我仍然需要知道類型。通過你的代碼,我擺脫了BaseVec,不需要使用'reinterpret_cast'和/或'dynamic_cast'。你能否解釋我的函數foo看起來會是什麼樣子?謝謝。 –

+0

pont是「te'與'A'類型一樣多的'function',因爲每個人都有自己的特性,所以不必切換,因爲你擁有的是void *和type信息是一個運行時的值void *消除了任何類型的信息,並且不是一個多態設計的好基礎 –

+0

你說得對,避免虛空會更好,但是,正如我所提到的,我沒有那麼多影響了傳給'foo'的東西,因爲這是由另一個程序決定的,儘管你讓我想到了一個完全不同的解決方案,它仍然可以使用你的答案,但是避免了void指針,我會試試這個需要一些時間 –

0

首先,在轉換爲/從指針轉換到指針時不要使用reinterpret_cast多態類。你可以寫一個簡單的指針包裝,讓你可以使用安全轉換操作符static_cast

template <class Type> 
class PointerWrapper 
{ 
public: 

    PointerWrapper(Type* object); 
    PointerWrapper& operator=(Type* object); 
    Type* operator->(); 

protected: 

    Type* object; 

}; 

template <class Type> 
PointerWrapper<Type>::PointerWrapper(Type* object) : 
    object(object) 
{ 
} 

template <class Type> 
PointerWrapper<Type>& PointerWrapper<Type>::operator=(Type* object) 
{ 
    this->object = object; 
} 

template <class Type> 
Type* PointerWrapper<Type>::operator->() 
{ 
    return object; 
} 

現在你可以這樣寫:

typedef PointerWrapper<BaseVec> BaseVecPointer; 

template<typename T> 
double foo(void* p) { 
    BaseVecPointer* vp = static_cast<BaseVecPointer*>(p); 
    // ... 
    // ... = (*vp)->get_double(...); 
    // ... 
    return result; 
} 

在這段代碼多態性功能被使用,即功能get_double被稱爲替代致電get

但是,如果你想打電話只是get,不get_double,也就是說你想打電話與根據運行時間變量的值不同的模板參數模板功能,可以使用下面的方法:

enum FooTypes 
{ 
    NoFooType = -1, 
    DoubleFooType = 0, 
    IntegerFooType = 1, 
    // ... 
    FooTypesCount 
}; 

template<FooTypes fooType> 
struct ChooseType 
{ 
    static 
    const FooTypes value = NoFooType; 

    typedef void Type; 
}; 

template<> 
struct ChooseType<DoubleFooType> 
{ 
    static 
    const FooTypes value = DoubleFooType; 

    typedef double Type; 
}; 

template<> 
struct ChooseType<IntegerFooType> 
{ 
    static 
    const FooTypes value = IntegerFooType; 

    typedef int Type; 
}; 

在這裏,您應該爲type變量的所有可能值編寫專業化的類模板ChooseType。 下面的代碼描述了選擇什麼foo_template函數模板專業化應該叫功能ChooseFoo

typedef double (*FooFunction)(void*); 

template<FooTypes fooType> 
FooFunction ChooseFooImpl(int type) 
{ 
    if (type == fooType) 
    { 
     if (ChooseType<fooType>::value != NoFooType) 
     { 
      return foo_template<typename ChooseType<fooType>::Type>; 
     } 
     else 
     { 
      return NULL; 
     } 
    } 
    else 
    { 
     return ChooseFooImpl<(FooTypes)(fooType - 1)>(type); 
    } 
} 

template<> 
FooFunction ChooseFooImpl<NoFooType>(int type) 
{ 
    return NULL; 
} 

FooFunction ChooseFoo(int type) 
{ 
    return ChooseFooImpl<FooTypesCount>(type); 
} 

這是foo功能實現:

double foo(void* p, int type) 
{ 
    FooFunction fooFunction = ChooseFoo(type); 

    if (fooFunction != NULL) 
    { 
     return fooFunction(p); 
    } 
    else 
    { 
     //unsupported type 
     // ... 
    } 
} 
+0

謝謝,這看起來很有希望,我會嘗試一下,它只是在我的C++知識的邊界上,所以這可能需要一些時間。 –