比较使用不同分配器的STL字符串

时间:2012-10-09 18:02:09

标签: c++ string memory-management stl

我想比较用不同分配器分配的STL字符串,例如带有自定义STL分配器的字符串的普通std::string。不幸的是,通常情况operator==()在这种情况下不起作用:

// Custom STL allocator to allocate char's for string class
typedef MyAllocator<char> MyCharAllocator;

// Define an instance of this allocator
MyCharAllocator myAlloc;

// An STL string with custom allocator
typedef std::basic_string
<
    char, 
    std::char_traits<char>, 
    MyCharAllocator
> 
CustomAllocString;

std::string s1("Hello");
CustomAllocString s2("Hello", myAlloc);

if (s1 == s2)  // <--- ERROR: doesn't compile
   ...

特别是,MSVC10(VS2010 SP1)会发出以下错误消息:

  

错误C2678:二进制'==':找不到左侧的操作符   'std :: string'类型的操作数(或者没有可接受的转换)

所以,低级(不太可读)代码如下:

if (strcmp(s1.c_str(), s2.c_str()) == 0)
   ...

应该使用。

(在例如std::vector个不同分配的字符串的情况下,这也特别令人讨厌,其中通常的简单v[i] == w[j]语法不能使用。)

这对我来说似乎不太好,因为自定义分配器改变了请求字符串内存的方式,但是字符串类的接口(包括与operator==()的比较)是独立于字符串分配其内存的特定方式。

我在这里缺少什么吗? 在这种情况下,是否可以保持C ++高级接口和运算符重载?

2 个答案:

答案 0 :(得分:37)

使用std::lexicographical_compare进行小于比较:

bool const lt = std::lexicographical_compare(s1.begin(), s1.end(),
                                             s2.begin(), s2.end());

对于相等比较,您可以使用std::equal

bool const e = s1.length() == s2.length() &&
               std::equal(s1.begin(), s1.end(), s2.begin());

或者,您可以回到strcmp(或实际memcmp,因为它具有正确的语义;请记住,C ++字符串比C字符串更通用),如您所建议的那样,它可以潜在地使用一些较低级别的魔法,例如一次比较整个机器字(尽管上述算法也可以是专用的)。我会说,测量和比较。对于短字符串,标准库算法至少具有很好的自描述性。


根据@ Dietmar的想法,你可以将这些函数包装成模板化的重载:

#include <string>
#include <algorithm>

template <typename TChar,
          typename TTraits1, typename TAlloc1,
          typename TTraits2, typename TAlloc2>
bool operator==(std::basic_string<TChar, TTraits1, TAlloc1> const & s1,
                std::basic_string<TChar, TTraits2, TAlloc2> const & s2)
{
    return s1.length() == s2.length() &&
           std::equal(s1.begin(), s1.end(), s2.begin());
}

用法示例:

#include <ext/malloc_allocator.h>
int main()
{
    std::string a("hello");
    std::basic_string<char, std::char_traits<char>, __gnu_cxx::malloc_allocator<char>> b("hello");
    return a == b;
}

实际上,您可以为大多数标准容器定义这样的重载。你甚至可以在模板上模板化,但那将是极端的。

答案 1 :(得分:19)

标准仅定义使用同源字符串类型的运算符,即,所有模板参数都需要匹配。但是,您可以在定义分配器的命名空间中定义合适的相等运算符:依赖于参数的查找将在那里找到它。如果您选择实现自己的赋值运算符,它将如下所示:

bool operator== (std::string const& s0,
                 std::basic_string<char, std::char_traits<char>, MyCharAllocator> const& s1) {
    return s0.size() == s1.size() && std::equal(s0.begin(), s0.end(), s1.begin()).first;
}

(加上一些其他重载)。将此提升到新的水平,根据容器需求定义各种关系运算符的版本甚至不限制模板参数甚至是合理的:

namespace my_alloc {
    template <typename T> class allocator { ... };
    template <typename T0, typename T1>
    bool operator== (T0 const& c0, T1 const& c1) {
        return c0.size() == c1.size() && std::equal(c0.begin(), c0.end(), c1.end);
    }
    ...
}

显然,运营商可以限制在特定的容器类型中,只有它们的分配器模板参数不同。

关于标准没有定义混合类型比较的原因,不支持混合类型比较的主要原因可能是你实际上并不想在程序中混合分配器!也就是说,如果您需要使用分配器,则可以使用分配器类型,该类型封装动态多态分配策略并始终使用生成的分配器类型。这样做的原因是,否则您将获得不兼容的界面,或者您需要将所有内容都设为模板,即您希望保留某些级别的词汇类型。当然,即使只使用一个额外的分配器类型,您也有两种词汇表类型:默认实例化和特殊分配的实例化。

也就是说,还有另一个不支持混合类型比较的潜在原因:如果operator==()真正成为两个值之间的比较,就像分配器不同的情况那样,它可能会引发更广泛的定义价值平等:应该std::vector<T>() == std::deque<T>支持吗?如果没有,为什么不同分配器的字符串之间的比较是特殊的?当然,分配器是std::basic_string<C, T, A>的非显着属性,这可能是忽略它的一个很好的理由。我不确定是否应该支持混合类型比较。支持运算符(这可能扩展到除operator==()之外的其他运算符)对于仅在其分配器类型上不同的容器类型可能是合理的。