60

異なるアロケータで割り当てられた 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++ の高レベル インターフェイスと演算子のオーバーロードを維持することは可能ですか?

4

2 に答える 2

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

実際、ほとんどの標準コンテナーに対してこのようなオーバーロードを定義できます。テンプレートでテンプレート化することもできますが、それは極端です。

于 2012-10-09T18:07:12.787 に答える
20

標準では、同種の文字列型を使用する演算子のみを定義します。つまり、すべてのテンプレート引数が一致する必要があります。ただし、アロケーターが定義されている名前空間で適切な等値演算子を定義できます。引数依存のルックアップはそこでそれを見つけます。独自の代入演算子を実装する場合は、次のようになります。

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

明らかに、アロケーター テンプレート パラメーターのみが異なる、特定のコンテナー タイプに演算子を制限することができます。

標準で混合型比較が定義されていない理由に関して、混合型比較がサポートされていない主な理由は、そもそもプログラム内でアロケーターを実際に混合したくないということです。つまり、アロケーターを使用する必要がある場合は、動的にポリモーフィックな割り当てポリシーをカプセル化するアロケーター タイプを使用し、結果のアロケーター タイプを常に使用します。そうしないと、互換性のないインターフェイスが得られるか、すべてをテンプレートにする必要があります。つまり、使用されている語彙タイプをある程度保持したいということです。もちろん、追加のアロケーター型を 1 つでも使用すると、デフォルトのインスタンス化と特別な割り当てのインスタンス化という 2 つのボキャブラリ文字列型を持つことになります。

とは言うものの、混合型比較をサポートしない別の潜在的な理由があります:operator==()アロケーターが異なる場合のように、実際に 2 つの値の比較になる場合、値の等価性のより広い定義が生じる可能性があります:std::vector<T>() == std::deque<T>サポートする必要がありますか? そうでない場合、異なるアロケーターを持つ文字列間の比較が特別になるのはなぜですか? もちろん、アロケーターは目立たない属性でstd::basic_string<C, T, A>あり、それを無視する正当な理由になる可能性があります。混合タイプの比較をサポートする必要があるかどうかはわかりません。operator==()アロケータ型のみが異なるコンテナ型の演算子をサポートすることは合理的かもしれません (これはおそらく 以外の演算子にも拡張されます)。

于 2012-10-09T18:33:19.640 に答える