C++ сортирует вектор с использованием функционального объекта

Я пытаюсь отсортировать вектор v1, используя другой вектор v2. Я не могу обдумать эту ошибку:

завершение вызывается после создания экземпляра 'std::out_of_range'
what(): vector::_M_range_check
Ловушка прерывания

при запуске этого кода:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Comp
{
    public:
        Comp(vector<double>& inVec): _V(inVec) {}
        bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}
    private:
        vector<double> _V;
};

int main(int argc, char** argv)
{
    double x1[] = {90.0, 100.0, 80.0};
    double x2[] = {9.0, 3.0, 1.0};
    vector<double> v1(x1,x1+3);
    vector<double> v2(x2,x2+3);

    sort(v1.begin(), v1.end(), Comp(v2));  // sort v1 according to v2

    for(unsigned int i=0; i<v1.size(); i++)
    {
        cout << v1.at(i) << " " << v2.at(i) << endl;
    }

    return 0;
}

v1 и v2 имеют одинаковый размер. Почему ошибка out_of_range?

Заранее спасибо за любые указатели.


person ram    schedule 28.07.2011    source источник
comment
ваш класс Comp должен содержать ссылку на вектор, а не копировать его.   -  person Tim    schedule 29.07.2011


Ответы (4)


Я считаю, что ваша проблема в этой строке:

bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}

Проблема в том, что когда алгоритм std::sort использует пользовательский обратный вызов, он передает фактические значения, хранящиеся в vector в определенных местах, а не индексы этих мест в vector. В результате, когда вы звоните

sort(v1.begin(), v1.end(), Comp(v2));  // sort v1 according to v2

Написанный вами компаратор Comp будет передавать в качестве параметров значения, хранящиеся в векторе v1, а затем попытается индексировать эти позиции в векторе v2. Поскольку значения в v1 больше, чем размер v2, вызов _V.at(i) вызовет исключение out_of_range.

Если вы хотите отсортировать два диапазона относительно друг друга, вам нужно применить другой подход. Я не знаю простого способа сделать это, но я дам вам знать, если придумаю.

person templatetypedef    schedule 28.07.2011

Размер v1 равен всего 3, но вы используете каждое значение v2 как индекс v1. И поскольку v2 имеет одно значение 9, которое больше, чем размер v1, именно это дает здесь ошибку std::out_of_range:

bool operator()(int i, int j) {return (_V.at(i)<_V.at(j));}

Функция std::vector::at выдает std::out_of_range исключение переданного ей индекса, поскольку аргумент больше размера вектора. То есть индекс должен быть меньше vector::size().

person Nawaz    schedule 28.07.2011

Хорошо, теперь вы, вероятно, знаете, что i и j являются фактическими значениями, хранящимися в vector, а не индексами. Для этого есть веская причина: сортировка касается значений, а не индексов. Обратите внимание, что вы передаете итераторы методу sort, поэтому он никак не может извлечь индекс для вас. Конечно, можно было бы получить индекс относительно первого итератора, но в этом нет смысла.

Однако давайте на некоторое время сойдем с ума и представим, что вы получите индексы, а не значения в вашем компараторе. Предположим, что ваш код делает то, что вы хотите, и давайте подумаем о следующем сценарии:

v1 = {20,10}; v2 = {2,1}

Я тайно предполагаю, что вы хотите получить следующий результат:

v1 = {10, 20}

Правильно? Теперь представьте, что я вызываю функцию сортировки, и я делаю следующие шаги:

  • v2[0] < v2[1] ложно, поэтому swap(&v1[0], &v1[1])

Это отсортировано, не так ли? Но подождите, я сумасшедшая функция сортировки, поэтому я хочу убедиться, что она отсортирована, поэтому я делаю следующее:

  • v2[0] < v2[1] неверно, swap(&v1[0], &v1[1])

И опять:

  • v2[0] < v2[1] неверно, swap(&v1[0], &v1[1])

и снова, снова, снова...

Вы видите проблему? У функции сортировки есть некоторые требования, и вы наверняка нарушаете фундаментальное требование.

Я подозреваю, что вам нужен совершенно другой контейнер (может быть, std::map с ключами из vec1 и значениями из vec2) или хотя бы что-то вроде vector< pair<double, double> >, чтобы вы могли легко сортировать по первому или второму значению. Если нет, рассмотрите возможность создания vector со значениями в диапазоне [0, v2.size()), отсортируйте его с помощью вашего компаратора (значения равны индексам, так что все будет в порядке), а затем напечатайте правильные значения из v1. Этот код работает нормально:

vector<size_t> indices;
for(size_t i =0; i < v1.size(); ++i)
{
  indices.push_back(i);
}

// yes, it works using your original comparator
sort(indices.begin(), indices.end(), Comp(v2));

for(size_t i =0; i < indices.size(); ++i)
{
    cout << v1.at(indices[i]) << " " << v2.at(indices[i]) << endl;
}
person tomasz    schedule 29.07.2011
comment
+1 за указание реальной причины, а не просто за то, что std::sort использует значения вместо индексов. - person leftaroundabout; 29.07.2011

Как сказано в других ответах, проблема заключается в том, что алгоритм сортировки передает фактические значения для сравнения, а не индексы.

Вот как вы можете решить эту проблему:

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

using namespace std;

typedef pair<double, double> Zipped; // Represent an element of two lists
                                     // "zipped" together

// Compare the second values of two pairs
bool compareSeconds ( Zipped i, Zipped j )
{
    return i.second < j.second;
}

int main ( int argc, char **argv )
{
    double x1[] = { 90, 100, 80 };
    double x2[] = { 9, 3, 1 };

    vector<double> v1(x1, x1 + 3);
    vector<double> v2(x2, x2 + 3);

    vector<Zipped> zipped(v1.size()); // This will be the zipped form of v1
                                      // and v2

    for ( int i = 0; i < zipped.size(); ++i )
    {
        zipped[i] = Zipped(v1[i], v2[i]);
    }

    sort(zipped.begin(), zipped.end(), &compareSeconds);

    for ( int i = 0; i < zipped.size(); ++i )
    {
        cout << zipped[i].first << " " << zipped[i].second << endl;
    }

    for ( int i = 0; i < v1.size(); ++i )
    {
        v1[i] = zipped[i].first;
    }

    // At this point, v1 is sorted according to v2

    return 0;
}
person rovaughn    schedule 29.07.2011