8

この質問に続いて、私はVS2010でここにある例をコピーして貼り付けようとしました:

#include <algorithm>
#include <vector>
#include <iostream>

struct S
{
    int number;
    char name;

    S ( int number, char name  )
        : number ( number ), name ( name )
    {}

    // only the number is relevant with this comparison
    bool operator< ( const S& s ) const
    {
        return number < s.number;
    }
};

struct Comp
{
    bool operator() ( const S& s, int i )
    {
        return s.number < i;
    }

    bool operator() ( int i, const S& s )
    {
        return i < s.number;
    }
};

int main()
{
    std::vector<S> vec = { {1,'A'}, {2,'B'}, {2,'C'}, {2,'D'}, {3,'F'}, {4,'G'} }; //this syntax won't compile in VS2010, so you can leave an empty vector here

    auto p = std::equal_range(vec.begin(),vec.end(),2,Comp());

    for ( auto i = p.first; i != p.second; ++i )
        std::cout << i->name << ' ';
}

これはリリース モードでは正常にコンパイルされますが、デバッグ モードではコンパイルに失敗します。その理由は、デバッグ モードでは、指定された述語を使用して、反復子の範囲が既に並べ替えられているかどうかを実装が確認するためです。

template<class _FwdIt,
    class _Pr> inline
    void _Debug_order2(_FwdIt _First, _FwdIt _Last, _Pr _Pred,
        _Dbfile_t _File, _Dbline_t _Line, forward_iterator_tag)
    {   // test if range is ordered by predicate, forward iterators
    for (_FwdIt _Next = _First; _First != _Last && ++_Next != _Last; ++_First)
        if (_DEBUG_LT_PRED(_Pred, *_Next, *_First))
            _DEBUG_ERROR2("sequence not ordered", _File, _Line);
    }

これは最終的に次のように呼び出します:

template<class _Pr, class _Ty1, class _Ty2> inline
    bool _Debug_lt_pred(_Pr _Pred,
        const _Ty1& _Left, const _Ty2& _Right,
        _Dbfile_t _File, _Dbline_t _Line)
    {   // test if _Pred(_Left, _Right) and _Pred is strict weak ordering
    if (!_Pred(_Left, _Right))
        return (false);
    else if (_Pred(_Right, _Left))
        _DEBUG_ERROR2("invalid operator<", _File, _Line);
    return (true);
    }

私の場合を除いて、operator()左と右の両方の「S」引数を取ることはできません。では、Visual の実装にバグはありますか? それとも、元の例は移植可能ではないのでしょうか? 3番目の operator() オーバーロードを提供することで機能させることができると思いますが、なくても機能するはずです

ありがとう

4

5 に答える 5

12

標準では、範囲内の 2 つのオブジェクトでコンパレーターを呼び出す必要はありません。これは、VS 2010 で使用される標準ライブラリのバグです。

関連するすべての要件は次のとおりです(C++ 11を引用):

[下界]§1+2:

1必須:の要素e[first,last)、式 ... に関して分割されます comp(e, value)

2戻り値:i範囲内[first,last]の任意の反復子について、次の対応する条件が保持jされる、範囲内の最も遠い反復子[first,i): ... comp(*j, value) != false.

[上限]§1+2:

1必須:の要素e[first,last)、式 ... に関して分割されます!comp(value, e)

2戻り値:i範囲内[first,last]の任意の反復子について、次の対応する条件が保持jされる、範囲内の最も遠い反復子[first,i): ... comp(value, *j) == false.

[等しい範囲]§1+2:

1必須:の要素eは、式 ...および[first,last)に関して分割されます。また、 のすべての要素について、 ...を意味するものとします。comp(e, value)!comp(value, e)e[first, last)comp(e, value)!comp(value, e)

2返品:

...

make_pair(lower_bound(first, last, value, comp),
          upper_bound(first, last, value, comp))

(省略記号は、非コンパレータ バージョン用です)。

于 2013-09-12T15:08:30.377 に答える
2

Angew はすでに標準を引用しており、VS にはバグがあると指摘しています。VS2010 には 2 つのバグがあることを強調したいだけです(デバッグ モードで)。

  1. Compare::operator(_Ty1, _Ty2)標準で存在する必要のないものを使用しようとします。この既知のバグは、Angew によって強調され、Ben のコメントによって指摘されました。

  2. 入力範囲がソートされているかどうかをテストしますが、これは標準でも要求されていません。これははるかに深刻なバグです。これにより、 の使いやすさが低下equal_rangeし、最悪の場合、アルゴリズム的には必要ない場合でも、範囲の完全な並べ替えが必要になります。おそらく、誰かがバグ レポートを MS に提出できますか?

最初のバグは、2 番目のバグの ( の実装における) 単なる結果であることに注意してくださいequal_range。おそらく、ほとんどのアプリケーションでは、入力範囲はすでにソートされています。これは、ユーザーが不必要にソートしたためであり(MS のライブラリのバグにより、ユーザー コードが正しくないことを示しています)、追加の比較演算子を提供するだけで問題が修正されます (OP の質問について)。 )。

于 2013-09-12T16:35:04.783 に答える
1

[lib.equal.range] は次のように伝えます:

必須: タイプ T は LessThanComparable (20.1.2) です。

効果: 最大の部分範囲 [i, j) を見つけて、順序に違反することなくその中の任意の反復子 k に値を挿入できるようにします。k は、対応する条件 !(*k < 値) && !(値 < *k) または comp(*k, 値) == false && comp(value, *k) == false を満たします。

コードは両方の条件を満たし、正常にコンパイルされるはずです。つまり、Visual Studio のバグです。

于 2013-09-12T15:23:23.507 に答える
1

一般的な回答として。各コンパイラと標準ライブラリには、標準を適切に実装していない癖や場所があります。つまり、コードが別のプラットフォームでテストされない限り、小さな変更が必要になる可能性が常にあるということです。

プラス面として、標準を維持しようとする場合、これらの変更は非常に小さいはずです。

于 2013-09-12T15:31:15.530 に答える
-1

等しい範囲が正しく機能するためには、範囲をソートする必要があります.VSコンパイラはあなたの意図が間違っていると言っていると思います.

于 2013-09-12T15:15:05.793 に答える