我正在尝试推出一种编写字符串算法的技术,该技术真正独立于字符串的基础类型。
背景: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&lt;&gt;,const char *,const wchar_t *(并且应该很容易扩展到std :: string)
2.我不想通过副本传递任何内容(仅限const&amp;或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&lt;&gt;时,T是CStringT&lt;&gt;的副本。而不是对它的引用:因此我们有一个不必要的副本。
尝试将上述内容重写为:
template <typename T>
inline int FindIndexOfOneOf(T & str, const CHARTYPEOF(T) * pszSearchChars)
{
return GetIndexOf(FindOneOf(str, pszSearchChars), str);
}
使编译器(VS2008)无法生成FindIndexOfOneOf&lt;&gt;的正确实例。为:
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&lt;&gt;&amp;)突出显示。
STL / std库“解决”了这个问题(如果真的可以称之为解决),而是使用遍布各处的迭代器而不是对事物本身的引用。我可以走这条路,除了它糟透了IMO,我不想让我的代码乱丢两个参数到处都应该有一个正确处理的单个参数。
基本上,我对一种方法很感兴趣 - 比如使用某种stringy_traits - 这样我就可以编写GetIndexOfOneOf&lt;&gt; (和其他类似的模板函数),其中参数是字符串(不是一对(正,结束)参数),然后根据该字符串参数类型生成的模板是正确的( const * 或 const CString&lt;&gt;&amp; )。
所以问题:我如何写FindIndexOfOneOf&lt;&gt;这样,如果没有创建基础参数的副本,它的参数可以是以下任何一个:
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
答案 0 :(得分: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
If is_array<U>::value is true, the modified-type type is remove_extent<U>::type *.
答案 1 :(得分:1)
您可以使用Boost的enable_if和type_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);
}
你应该扩展第一个案例来处理char
和wchar_t
字符串,你可以使用or_
from Boost's MPL library来完成。
我还建议使用引用的版本代替const引用。这只是避免实例化2个单独版本的代码(因为它代表,T
将被推断为const对象的const类型,非const类型用于非const对象;将参数类型更改为{ {1}}表示T const& str
将始终被推断为非const类型。)
答案 2 :(得分: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));
}
答案 3 :(得分: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;
};