比较使用不同分配器的STLstring
我想比较使用不同的分配器分配的STLstring,例如普通的std::string
与使用自定义STL分配器的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:二进制'==':找不到操作符findtypes为'std :: string'的左手操作数(或者没有可接受的转换)
所以,像这样的较低级别 (不太可读)的代码:
if (strcmp(s1.c_str(), s2.c_str()) == 0) ...
应该使用。
(这也是特别恼人的情况下,例如有不同的分配的string的std::vector
,其中通常简单的v[i] == w[j]
语法不能使用)。
这对我来说似乎不是很好,因为自定义的分配器会改变请求的string内存的方式,但是string类的接口 (包括与operator==()
比较)是独立于string分配内存的特定方式。
有什么我在这里失踪? 在这种情况下是否可以保持C ++高级接口和运算符重载?
使用std::lexicographical_compare
进行less于比较:
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 ++string比Cstring更普遍),正如你所build议的那样,可能会使用一些更低级别的魔法,比如一次一个整个机器字(尽pipe上述algorithm也可以是专用的)。 衡量和比较,我会说。 对于短string,标准库algorithm至less是很好的自描述。
基于@ 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; }
实际上,您可以为大多数标准容器定义这样的过载。 你甚至可以在模板上模板,但这是极端的。
该标准仅定义了使用同类stringtypes的运算符,即所有的模板参数都需要匹配。 但是,您可以在定义分配器的名称空间中定义合适的相等运算符:参数相关的查找将在其中find它。 如果你select实现你自己的赋值操作符,它看起来像这样:
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); } ... }
显然,运算符可以被限制到特定的容器types,只有它们的分配器模板参数不同。
关于为什么标准没有定义混合types比较,不支持混合types比较的主要原因可能是您实际上不希望在您的程序中混合使用分配器! 也就是说,如果你需要使用分配器,你可以使用分配器types来封装一个dynamic的多态分配策略,并且总是使用结果分配器types。 推理是这样的,否则你会得到不兼容的接口,或者你需要使所有的东西都成为一个模板,也就是说,你想保留一些使用的词汇types。 当然,即使只使用一个额外的分配器types,也会有两个词汇表stringtypes:默认实例化和特殊分配的实例化。
也就是说,还有另外一个可能的原因是不支持混合types比较:如果operator==()
真的成为两个值之间的比较,就像分配器不同的情况一样,这可能会引发更广泛的值相等的定义:应该支持std::vector<T>() == std::deque<T>
吗? 如果不是的话,为什么不同分配器的string之间的比较是特殊的? 当然,分配器是std::basic_string<C, T, A>
的非显着属性,这可能是忽略它的一个很好的理由。 我不知道是否应该支持混合types比较。 对于仅在分配器types上不同的容器types,支持运算符(这可能会延伸到除operator==()
之外的其他运算operator==()
可能是合理的。