【发布时间】:2011-05-17 09:41:54
【问题描述】:
我正在尝试推导一种编写字符串算法的技术,该技术真正独立于字符串的底层类型。
背景: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 *、const 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 时,T 是 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] vs. CString & 最能突出显示)。
STL/std 库通过在各处使用迭代器对而不是对事物本身的引用来“解决”了这个问题(如果真的可以称之为解决的话)。我可以走这条路,除非它很糟糕 IMO,而且我不想在任何应该正确处理单个参数的地方都用两个参数乱扔我的代码。
基本上,我对一种方法感兴趣 - 例如使用某种 stringy_traits - 这将允许我编写 GetIndexOfOneOf (和其他类似的模板函数),其中参数是字符串(不是一对 ( , end] 参数),然后根据该字符串参数类型(const * 或 const CString &)生成的模板是正确的。
所以问题:我如何编写 FindIndexOfOneOf 使其参数可以是以下任何一种,而无需创建基础参数的副本:
1. FindIndexOfOneOf(_T("abc"), _T("def"));
2. CString 字符串; FindIndexOfOneOf(str, _T("def"));
3. CString 字符串; FindIndexOfOneOf(T("abc"), str);
3. CString 字符串; FindIndexOfOneOf(str, str);
与此相关的线程将我引向这一点:
A better way to declare a char-type appropriate CString<>
Templated string literals
【问题讨论】:
-
个人喜好。我已经知道该怎么做,我觉得它很难看,我想看看是否有真正更聪明的方法。
-
避免使用迭代器(除了个人偏好)的部分原因是我已经有很多客户端代码希望能够传递 CStrings 和 const char *s。所以我将不得不更新大量的客户端代码。在这一点上,编写每个函数的两个版本更有意义,用于窄和宽,并允许自动类型转换以强制 CString 为 const
*s.