Как обрабатывать std :: find_if (), возвращая false? - PullRequest
4 голосов
/ 06 мая 2019

Возьмите следующий пример, взятый из справочной страницы cplusplus.com и измененный для возврата false:

// find_if example
#include <iostream>     // std::cout
#include <algorithm>    // std::find_if
#include <vector>       // std::vector

bool IsOdd (int i) {
  return ((i%2)==1);
}

int main ()
{
  std::vector<int> myvector;    
  myvector.push_back(10);
  myvector.push_back(20);
  myvector.push_back(40);
  myvector.push_back(50);

  std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
  std::cout << "The first odd value is " << *it << '\n';

  return 0;
} 

Поскольку никакое значение в myvector не является нечетным, возвращается InputIterator last , который не определен:

The first odd value is -1727673935

Как правильно обрабатывать этот вывод?

Как я могу узнать, std::find_if() вернул false, если выходные данные непредсказуемы и сравнение со всем вектором для подтверждения, что результирующее значение не существует, отрицает цель использования std::find_if() для начала?

Ответы [ 4 ]

8 голосов
/ 06 мая 2019

Вы имеете в виду

std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);

if ( it != myvector.end() )
{
    std::cout << "The first odd value is " << *it << '\n';
}
else
{
    // std::cout << "there is no odd value in the vector\n";
}
5 голосов
/ 06 мая 2019

Вам необходимо проверить, является ли возвращенный итератор конечным итератором, который вы передали std::find_if (второй аргумент). Эта семантика довольно распространена для алгоритмов в стандартной библиотеке, поэтому вам следует к этому привыкнуть.

const auto firstOdd = std::find_if (myvector.cbegin(), myvector.cend(), IsOdd);

if (firstOdd != myvector.cend())
    std::cout << "The first odd value is " << *it << '\n';
else
    std::cout << "No odd values found\n";

Обратите внимание, что вы можете использовать функции-члены cbegin() / cend(), поскольку вы не изменяете контейнер.

5 голосов
/ 06 мая 2019

Идиоматический способ сделать это - проверить, равен ли итератор конечному стражу.

auto it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
if (it == myvector.end()) {
    std::cout << "No odd values found" << std::endl;
} else {
    std::cout << "The first odd value is " << *it << std::endl;
}

В C ++ 17 (самый последний стандарт) вы можете объявить итератор прямо в операторе if:

if (auto it = std::find_if(myvector.begin(), myvector.end(), IsOdd); it != myvector.end()) {
    std::cout << "The first odd value is " << *it << std::endl;
} else {
    std::cout << "No odd values found" << std::endl;
}
4 голосов
/ 06 мая 2019

std :: find_if возвращает ( ссылка cppreference.com )

Итератор дляпервый элемент, удовлетворяющий условию, или последний, если нет такой элемент найден .

Это означает, что разыменовывать итератор можно только тогда, когда его не равенcontainer.end () iterator.

if (const auto iter = std::find_if(myvector.cbegin(), myvector.cend(), IsOdd); // need C++17 compiler support
    iter != myvector.cend())
{
    std::cout << *iter << "\n";
}
else
{
    // code
}

PS : В современном C ++ выражения lambdas должны быть вашими хорошими друзьями и использовать их, когда это уместно.Подробнее здесь: Почему лямбды могут быть лучше оптимизированы компилятором, чем обычные функции?

Это означает, что ваш IsOdd мог бы быть

constexpr auto isOdd = [](const int i) /* noexcept */ { return i & 1; };
...