2010-11-29 63 views
0

我試圖推導寫串的算法,是真正獨立於底層字符串類型的技術。C++字符串類型獨立算法

背景:GetIndexOf和FindOneOf原型要麼重載或模板的變化:

int GetIndexOf(const char * pszInner, const char * pszString); 
const char * FindOneOf(const char * pszString, const char * pszSetOfChars); 

這個問題在下面的模板功能出現:

// return index of, or -1, the first occurrence of any given char in target 
template <typename T> 
inline int FindIndexOfOneOf(const T * str, const T * pszSearchChars) 
{ 
    return GetIndexOf(FindOneOf(str, pszSearchChars), str); 
} 

目標:
1。我想這個代碼爲CStringT <>,爲const char *,常量爲wchar_t *工作(也應該是微不足道延伸到的std :: string)
2.我不想通過複製任何東西(只有通過const &或const *)

在試圖解決這兩個目標,我想我可能能夠使用類型選擇器的各種得出的飛行正確的接口:

namespace details { 

    template <typename T> 
    struct char_type_of 
    { 
     // typedef T type; error for invalid types (i.e. anything for which there is not a specialization) 
    }; 

    template <> 
    struct char_type_of<const char *> 
    { 
     typedef char type; 
    }; 

    template <> 
    struct char_type_of<const wchar_t *> 
    { 
     typedef wchar_t type; 
    }; 

    template <> 
    struct char_type_of<CStringA> 
    { 
     typedef CStringA::XCHAR type; 
    }; 

    template <> 
    struct char_type_of<CStringW> 
    { 
     typedef CStringW::XCHAR type; 
    }; 

} 

#define CHARTYPEOF(T) typename details::char_type_of<T>::type 

其中允許:

template <typename T> 
inline int FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars) 
{ 
    return GetIndexOf(FindOneOf(str, pszSearchChars), str); 
} 

這應保證第二個參數爲const *過去了,不應該確定T(而不是隻有第一個參數應該確定T)。

但是這種方法的問題在於,當str是一個CStringT時,它是CStringT的副本,而不是對它的引用:因此我們有一個不必要的副本。

試圖重寫上面爲:

template <typename T> 
inline int FindIndexOfOneOf(T & str, const CHARTYPEOF(T) * pszSearchChars) 
{ 
    return GetIndexOf(FindOneOf(str, pszSearchChars), str); 
} 

使得不可能對於編譯器(VS2008)來生成FindIndexOfOneOf <的正確實例>爲:

FindIndexOfOneOf(_T("abc"), _T("def")); 
    error C2893: Failed to specialize function template 'int FindIndexOfOneOf(T &,const details::char_type_of<T>::type *)' 
    With the following template arguments: 'const char [4]' 

這是一個通用問題自引入它們以來,我已經使用過模板(是的,我很老):構建一種處理舊的C風格數組和基於較新的基於類的實體的方法實際上是不可能的(可能最好由const char [ 4]與CString <> &)。

的STL/STD庫「解決」這個問題(如果確實可以稱之爲解決),而不是使用迭代器對無處不在,而不是事物本身的參考。我可以走這條路,除非它吸引IMO,而且我不想用兩個參數來拋棄我的代碼,無論哪裏都應該有正確處理的單一論點。

基本上,我感興趣的方法 - 如使用某種stringy_traits - 這將允許我寫GetIndexOfOneOf <>(和其他類似的模板函數)其中參數是字符串(不是一對(然後根據該字符串參數類型(const *const CString <> &)生成的模板是正確的。

於是問:如何可能我寫FindIndexOfOneOf <>使得它的參數可以是下列任何一項而沒有建立的基本參數的副本:
1. FindIndexOfOneOf(_T(「ABC」), _T( 「DEF」));
2. CString str; FindIndexOfOneOf(str,_T(「def」));
3. CString str; FindIndexOfOneOf(T(「abc」),str);
3. CString str; FindIndexOfOneOf(str,str);

相關線程這其中有帶領我到了這一點:

A better way to declare a char-type appropriate CString<>
Templated string literals

+1

爲什麼不使用迭代器? – 2010-11-29 17:14:54

+0

個人喜好。我已經知道如何做到這一點,我覺得它很難看,我想看看是否有一個真正聰明的方法。 – Mordachai 2010-11-29 17:19:09

回答

2

試試這個。

#include <type_traits> 
inline int FindIndexOfOneOf(T& str, const typename char_type_of<typename std::decay<T>::type>::type* pszSearchChars) 

的問題是,當你做的第一個參數引用類型T成爲推導出:

const char [] 

但你要

const char* 

您可以使用下面的方法使這個轉換。

std::decay<T>::type 

documentation說。

If is_array<U>::value is true, the modified-type type is remove_extent<U>::type *. 
1

您可以使用Boost的enable_iftype_traits此:

#include <boost/type_traits.hpp> 
#include <boost/utility/enable_if.hpp> 

// Just for convenience 
using boost::enable_if; 
using boost::disable_if; 
using boost::is_same; 

// Version for C strings takes param #1 by value 
template <typename T> 
inline typename enable_if<is_same<T, const char*>, int>::type 
FindIndexOfOneOf(T str, const CHARTYPEOF(T) * pszSearchChars) 
{ 
    return GetIndexOf(FindOneOf(str, pszSearchChars), str); 
} 

// Version for other types takes param #1 by ref 
template <typename T> 
inline typename disable_if<is_same<T, const char*>, int>::type 
FindIndexOfOneOf(T& str, const CHARTYPEOF(T) * pszSearchChars) 
{ 
    return GetIndexOf(FindOneOf(str, pszSearchChars), str); 
} 

你或許應該擴大第一種情況下同時處理charwchar_t字符串,您可以使用or_ from Boost's MPL library做。

我也建議讓版本引用一個const引用來代替。這只是避免實例化2個獨立版本的代碼(因爲它代表,T將被推斷爲const對象的const類型,而非const類型則被推斷爲非const對象;將參數類型更改爲T const& str意味着T將始終被推斷爲非常量類型)。

1

根據你對迭代器的評論,似乎你還沒有完全考慮你可能擁有的選項。我無法做任何關於個人偏好的事情,但是再次...恕我直言,它不應該是一個難以克服的障礙,以接受合理的解決方案,應該權衡和平衡技術上

template < typename Iter > 
void my_iter_fun(Iter start, Iter end) 
{ 
... 
} 
template < typename T > 
void my_string_interface(T str) 
{ 
    my_iter_fun(str.begin(), str.end()); 
} 
template < typename T > 
void my_string_interface(T* chars) 
{ 
    my_iter_fun(chars, chars + strlen(chars)); 
} 
1

替代我以前的答案,如果你不想安裝tr1。

當第一個參數是引用時,添加以下模板特化項以覆蓋推導的T類型。

template<unsigned int N> 
struct char_type_of<const wchar_t[N]> 
{ 
    typedef wchar_t type; 
}; 

template<unsigned int N> 
struct char_type_of<const char[N]> 
{ 
    typedef char type; 
};