2009-01-09 51 views
2

我想這樣做:
func(conditionA?pa1:pa2,conditionB?pb1:pb2,conditionC?pc1:pc2);C++模板可以爲條件來改善代碼嗎?

在C風格功能中,沒有問題。但是,如果func()是一個模板函數,編譯器會報告錯誤。 這裏pa1和pa2,...是不同的類,並有一個靜態方法 - 「convert()」。考慮到性能,convert()也被聲明爲inline

如果模板無法解決這個問題,將會有一個非常looooooooooong if-else如下所示。

 
if (conditionA) 
{ 
    typeA1 a; 
    if (conditionB) 
    { 
     typeB1 b; 
     if (conditonC) 
     { 
      C1 c; 
      Function(a, b, c); 
     } 
     else 
     { 
      C2 c; 
      Function(a, b, c); 
     } 
    } 
    else 
    { 
     typeB2 b; 
     if (conditonC) 
     { 
      C1 c; 
      Function(a, b, c); 
     } 
     else 
     { 
      C2 c; 
      Function(a, b, c); 
     } 
    } 
} 
else 
{ 
    typeA2 a; 
    if (conditionB) 
    { 
     typeB1 b; 
     if (conditonC) 
     { 
      C1 c; 
      Function(a, b, c); 
     } 
     else 
     { 
      C2 c; 
      Function(a, b, c); 
     } 
    } 
    else 
    { 
     typeB2 b; 
     if (conditonC) 
     { 
      C1 c; 
      Function(a, b, c); 
     } 
     else 
     { 
      C2 c; 
      Function(a, b, c); 
     } 
    } 
} 
+0

的條件是編譯時間常數? – 2009-01-09 03:10:20

回答

3

條件運算的結果(即,在p ? a : bab)必須是相同的類型。也就是說,你不能做:

predicate() ? 3.14 : "sdfsd" 

確保您pa1pa2是兼容的類型(或者它們是同一類型,從一種類型的繼承,或者轉換爲兼容的類型)。如果你確實有一個convert成員函數轉換這些類型兼容的類型,那麼爲什麼不直接使用:

conditionA ? pa1.convert() : pa2.convert() 

最後,它不是SOOOOO長。你已經寫出了定義。只是保持通用並繼續前進。

2

使pa1和pa2從一個公共基類繼承,並使用對該祖先的引用作爲您的(然後非模板化)函數的參數類型。

1

您遇到的問題是編譯時輸入較強,您希望在運行時更改類型。當編譯Function的模板時,它需要知道提供什麼類型。你可以使用不同的類型,你不能在1行中完成。

做一個convert(),你真的想花這麼多錢,只有在Function中需要的時候呢?

步進從模板了......

你的描述比它似乎更糟糕的方法的長度,你需要將已經定義了一個的B的和C的。您也可以將條件縮減爲switch語句。

如果將a,b和c保留爲完整對象,並且可以要求它將值傳遞給該函數。

class a { 
    p1, p2; 
    condition; 
    val value() { return condition? p1.convert, p2.convert }; 
} 

您可以讓他們有一個通用的函數所需的接口。有幾種方法可以做到這一點,但最簡單的方法是,如果您可以更改TypeA1的類,請在此處添加類似IConvertable的父類。

class IConvertable{ 
public: 
    ~IConvertable(){} 
    virtual val convert() = 0; 
} 

然後在每個類中執行轉換來調用靜態版本的convert。

1

這是一個有點難看,但使用位,你可以把你的3級嵌套到一個單一的開關語句:

const unsigned int caseA = 1 << 0; 
const unsigned int caseB = 1 << 1; 
const unsigned int caseC = 1 << 2; 
switch ((conditionA ? caseA : 0) | (conditionB ? caseB : 0) | (conditionC ? caseC : 0)) { 
    case 0:       func(pa2, pb2, pc2); break; 
    case caseA:      func(pa1, pb2, pc2); break; 
    case caseB:      func(pa2, pb1, pc2); break; 
    case caseA|caseB:   func(pa1, pb1, pc2); break; 
    case caseC:      func(pa2, pb2, pa1); break; 
    case caseA|caseC:   func(pa1, pb2, pc1); break; 
    case caseB|caseC:   func(pa2, pb1, pc1); break; 
    case caseA|caseB|caseC: func(pa1, pb1, pc1); break; 
    default: assert(false); // unreachable 
} 

這種分裂的系列3個的二元決策到一個單一的8路的決定,所以推理起來更簡單。有些人可能會討厭它,但我覺得它很可讀。

2

你的基本問題是表達式的類型在編譯時必須是可知的

條件在編譯時是否已知且已知?如果是這樣,這將是可以使用元函數來選擇每個參數:

template <bool B> 
struct choose { 
    typedef X type; 
}; 

template <> 
struct choose<false> { 
    typedef Y type; 
}; 

... 

func(choose<a_constant_bool_expr>::type()); 

(簡化1參數的情況下,對3個參數,你會定義如struct choose1struct choose2struct choose3。)

否則,你最好的選擇是從John Zwinck建議的一個共同基礎中派生所有類型。唯一的替代方案是龐大的switch聲明或if的列表。

其實,也許這是Boost.Variant會有好處的東西?

0

擴展上(重複),其他人都已經說...

不會工作:

template<class TYPE> 
inline void Function(TYPE & object) 
{ 
    cout << "Function(): " << object.convert() << endl; 
} 

class A 
{ 
public: 
    static const char * convert() { return "classA"; } 
}; 

class B 
{ 
public: 
    static const char * convert() { return "classB"; } 
}; 

int 
main(int argc) 
{ 
    A a; 
    B b; 

    Function(argc>1 ? a : b); 
} 

由於一個b是不同的類型,以及模板化函數正在爲您根據參數類型創建。

但是這將工作:

template<class TYPE> 
inline void Function(TYPE & object) 
{ 
    cout << "Function(): " << object.convert() << endl; 
} 

class C 
{ 
public: 
    virtual const char * convert() = 0; 
}; 

class A : public C 
{ 
public: 
    static const char * staticConvert() { return "classA"; } 
    const char * convert() { return A::staticConvert(); } 
}; 

class B : public C 
{ 
public: 
    static const char * staticConvert() { return "classB"; } 
    const char * convert() { return B::staticConvert(); } 
}; 

int 
main(int argc) 
{ 
    A a; 
    B b; 

    Function(argc>1 ? (C&)a : (C&)b); 
} 

雖然我真的應該使用dynamic_cast的...

Function(argc>1 ? dynamic_cast<C&>(a) : dynamic_cast<C&>(b))