2010-04-06 184 views
4

動機:我想創建一個實用工具類,以便而不必寫:C++模板類型推演問題

if(someVal == val1 || someVal == val2 || someVal == val3) 

我可以代替寫:

if(is(someVal).in(val1, val2, val3)) 

這是更接近數學的'a是(b,c,d)'的一個元素,並且當變量名'someVal'很長時也可以節省大量的輸入。

這裏是我迄今爲止(2倍3的值)的代碼:

template<class T> 
class is { 
private: 
    T t_; 
public: 
    is(T t) : t_(t) { } 

    bool in(const T& v1, const T& v2) { 
     return t_ == v1 || t_ == v2; 
    } 
    bool in(const T& v1, const T& v2, const T& v3) { 
     return t_ == v1 || t_ == v2 || t_ == v3; 
    } 
}; 

但是它無法對其進行編譯,如果我寫:

is(1).in(3,4,5); 

代替我必須寫

is<int>(1).in(3,4,5); 

這並不算太壞,但如果編譯器能夠發現類型爲會更好無需我明確指定它。
有沒有辦法做到這一點或我堅持明確指定它?

回答

14

如果你想保持這種語法,你可以像使用一個輔助功能:

template<class T> 
class is_op { 
private: 
    T t_; 
public: 
    is_op(T t) : t_(t) { } 

    bool in(const T& v1, const T& v2) { 
     return t_ == v1 || t_ == v2; 
    } 
    bool in(const T& v1, const T& v2, const T& v3) { 
     return t_ == v1 || t_ == v2 || t_ == v3; 
    } 
}; 


template< class U > 
inline is_op<U> is(U const& v) 
{ 
    return is_op<U>(v); 
} 

int main(int argc, char* argv[]) 
{ 
    is(1).in(1 , 2 , 4); 
} 
+1

典型的方法是編寫提供功能的類,然後編寫一個函數來創建實例該類自函數支持類型推導。 – 2010-04-06 09:35:09

0

你一直堅持它 - 構造函數要求提供的類型作爲模板參數。我應該看到,我真的不喜歡你的想法(尤其是班級名稱)。爲什麼不使用std:; set?甚至一個模板函數 - 是這樣的:

template <typename T> 
bool IsIn(T v, T a, T b, T c) { 
    ... 
} 
+0

事實上,我從類似於IsIn的東西開始,因爲一個名爲'in'的類似乎在尋求麻煩,但最終我決定爲了可讀性而選擇(a).in(b,c)。重新使用std :: set - 我的用例是我希望能夠在一行中做到這一點,不必向該集合添加成員,然後檢查成員資格。 (順便說一下,我沒有downvote你) – hamishmcn 2010-04-06 09:44:40

+0

關於類名:把它放在一個名稱空間,它會好嗎? (它有多大可能是C++會得到一個**是**關鍵字?!是不是所有人都稱之爲istreams?) – UncleBens 2010-04-06 13:28:32

1
template<typename T> 
is<T> is_value(T value) 
{ 
    return is<T>(value); 
} 

int main() 
{ 
    bool r ; 

    r = is_value(1).in(3,4,5); 
    r = is_value(3).in(3,4,5); 

    return 0; 
} 
5

問題是相當有趣的,這是真的布爾條件會變得多毛。

雖然我自己傾向於更喜歡編寫特殊功能,因爲這裏的含義很難傳達。做什麼:

if (someVal == val1 || someVal == val2 || someVal == val3) 

是什麼意思?

if (is(someval).in(val1, val2, val3)) 
// or 
if (is(someval).in(val1)(val2)(val3)) // implements short-circuiting 
             // and removes arity issue 
             // using a proxy object 

更好?

我認爲這將是更容易閱讀:

bool isToBeLogged(const Foo& foo) 
{ 
    // Either 
    static std::set<Foo> ValuesToLog = /* some boost assign magic or whatever */; 
    return ValuesToLog.find(foo) != ValuesToLog.end(); 

    // Or 
    return foo == val1 || foo == val2 || foo == val3; 
} 


if (isToBeLogged(someVal)) 

我想這是一個風格問題。

第二種方法的優點包括:

  • 萬一測試超過一次,邏輯是不是散落各地的(這樣的改變很容易)
  • 無需置評測試,方法名稱是否已經

Inconvients?我想這是更多的打字......哦,很好:p

+0

我不知道你會如何實現短路?您不會傳遞函數調用的結果,而是查找時調用函數的對象? - 但一般來說,你不能結合這些方法(使用is.in來實現isToBeLogged)嗎? – UncleBens 2010-04-06 11:46:39

0

您的比較類的修改可能是使用可變參數,使其通用於n個元素集。

+0

可變參數不適用於大多數類型的T? C++ 0x中的變量模板或'initializer_list'是另一回事。 – UncleBens 2010-04-06 13:30:35

1

對於廣大的問題,就像一個效用函數中包含可能會派上用場:

#include <boost/range.hpp> 
template <class Range, class T> 
bool contains(const Range& range, const T& value) 
{ 
    return std::find(boost::begin(range), boost::end(range), value) != boost::end(range); 
} 

(升壓使用使得它也接受陣列,儘管人們可能會單獨寫超載這也可能過載與容器。 。查找成員函數)

在C++ 0x中,這可能被擴展以支持std::initializer_list<T> *允許相當不錯的用法:

if (contains({1, 2, 3}, value) {...} 

*不知道它是否應該不能工作,但我的編譯器需要重載才能使其工作。