2010-11-29 89 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; 
};