Как std :: найти, используя объект сравнения? - PullRequest
6 голосов
/ 17 апреля 2010

Я запутался насчет интерфейса std::find. Почему он не берет Compare объект, который говорит ему, как сравнивать два объекта?

Если бы я мог передать объект Compare, я мог бы заставить работать следующий код, где я хотел бы сравнивать по значению, а не просто сравнивать значения указателя напрямую:

typedef std::vector<std::string*> Vec;
Vec vec;
std::string* s1 = new std::string("foo");
std::string* s2 = new std::string("foo");
vec.push_back(s1);
Vec::const_iterator found = std::find(vec.begin(), vec.end(), s2);
// not found, obviously, because I can't tell it to compare by value
delete s1;
delete s2;

Является ли следующий рекомендуемый способ сделать это?

template<class T>
struct MyEqualsByVal {
  const T& x_;
  MyEqualsByVal(const T& x) : x_(x) {}
  bool operator()(const T& y) const {
    return *x_ == *y;
  }
};
// ...
vec.push_back(s1);
Vec::const_iterator found = 
    std::find_if(vec.begin(), vec.end(),
                 MyEqualsByVal<std::string*>(s2)); // OK, will find "foo"

Ответы [ 3 ]

6 голосов
/ 17 апреля 2010

find нельзя перегрузить, чтобы получить унарный предикат вместо значения, потому что это необязательный параметр шаблона. Поэтому, если бы вы вызвали find(first, last, my_predicate), была бы потенциальная неоднозначность, хотите ли вы, чтобы предикат оценивался для каждого члена диапазона, или вы хотите найти элемент диапазона, равный самому предикату (это может быть диапазон предикатов, для всех разработчиков стандартных библиотек, которые знают или заботятся, или value_type итератора может быть конвертируемым как в тип предиката, так и в его argument_type). Отсюда необходимость для find_if перейти под отдельным именем.

find мог быть перегружен, чтобы принять необязательный двоичный предикат, в дополнение к искомому значению. Но захват значений в функторах, как вы уже сделали, является настолько стандартной техникой, что я не думаю, что это будет огромный выигрыш: это, безусловно, никогда не нужно, так как вы всегда можете достичь того же результата с find_if.

Если вы получили find, который вы хотели, вам все равно пришлось бы написать функтор (или использовать boost), поскольку <functional> не содержит ничего для разыменования указателя. Тем не менее, ваш функтор будет немного проще в качестве двоичного предиката, либо вы можете использовать указатель на функцию, так что это будет скромный выигрыш. Так что я не знаю, почему это не предусмотрено. Учитывая фиаско copy_if, я не уверен, что есть много смысла предполагать, что всегда есть веские причины для недоступных алгоритмов: -)

2 голосов
/ 17 апреля 2010

Поскольку ваш T является указателем, вы также можете сохранить копию указателя в объекте функции.

Кроме этого, так все и делается, и тут не так много.

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

0 голосов
/ 17 апреля 2010

Это именно то, для чего find_if - требуется предикат, который вызывается для сравнения элементов.

...