【问题标题】:C++ String-type independent algorithmsC++ 字符串类型独立算法
【发布时间】: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.

标签: c++ templates mfc


【解决方案1】:

试试这个。

#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 *.

【讨论】:

  • 此解决方案依赖于 VS 2010(它具有用于 C++0x 的 std 库的更新的临时版本)。我目前仍在 2008 年(但我认为这无论如何都来自 boost,我可以使用它)。
  • type_traits 包含在适用于 Visual Studio 2008 的 tr1 中。
【解决方案2】:

您可以为此使用 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 类型,以及非常量对象的非常量类型;将参数类型更改为 T const&amp; str表示T 将始终被推断为非常量类型)。

【讨论】:

    【解决方案3】:

    根据您关于迭代器的 cmets,您似乎没有充分考虑您可能拥有的选项。我对个人偏好无能为力,但话又说回来......恕我直言,为了接受合理的解决方案,它不应该是一个难以克服的障碍,应该在技术上权衡和平衡。

    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));
    }
    

    【讨论】:

      【解决方案4】:

      如果您不想安装 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;
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-24
        • 2013-08-27
        • 1970-01-01
        • 2011-07-01
        • 2010-11-08
        • 2022-01-16
        相关资源
        最近更新 更多