Also you don't return in the event of a not-found, which I think is undefined behaviour in C++ (as opposed to a compiler error, like in sensible languages).
To dust off my C++ experiences and throw together a stupidly-generic find example using the std::optional added in C++17,
#include <iostream>
#include <optional>
#include <vector>
#include <functional>
template<class InputIt> constexpr auto try_find_val(InputIt first, InputIt last, const typename std::iterator_traits<InputIt>::value_type & value ) -> std::optional<typename std::iterator_traits<InputIt>::value_type> {
for(auto i = first; i != last; ++i) {
if (*i == value) {
return *i;
}
}
return {};
}
void test_found()
{
std::cout << "Testing should be found" << std::endl;
std::vector<int> list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto result = try_find_val(std::begin(list), std::end(list), 10);
if (result) {
std::cout << "Success: Found " << *result << std::endl;
} else {
std::cout << "Error: Not found " << std::endl;
}
}
void test_not_found()
{
std::cout << "Testing should be not found" << std::endl;
std::vector<int> list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto result = try_find_val(std::begin(list), std::end(list), 12);
if (result) {
std::cout << "Error: Found " << *result << std::endl;
} else {
std::cout << "Success: Not found " << std::endl;
}
}
int main() {
test_found();
test_not_found();
return 0;
}
Naturally, if you know some or all the types you'll be working with beforehand you can simplify or remove a lot of the templated shenanigans for the extra readability.
You could also do it by ref to allow mutation, or const ref to prevent a copy:
template<class InputIt> auto try_find_ref(InputIt first, InputIt last, const typename std::iterator_traits<InputIt>::value_type& value ) -> std::optional<std::reference_wrapper<typename std::iterator_traits<InputIt>::value_type>> {
for(auto i = first; i != last; ++i) {
if (*i == value) {
return std::ref(*i);
}
}
return {};
}
template<class InputIt> auto try_find_cref(InputIt first, InputIt last, const typename std::iterator_traits<InputIt>::value_type & value ) -> std::optional<std::reference_wrapper<const typename std::iterator_traits<InputIt>::value_type>> {
for(auto i = first; i != last; ++i) {
if (*i == value) {
return std::cref(*i);
}
}
return {};
}
std::begin and std::end works for the standard collections and fixed sixed arrays.
std::begin/std::end don't work for naked dynamic arrays in the T* format (so creating using the T* arr = new T[N]).
You'd need to either pass in something like (arr, arr + N) instead of begin and end, or wrap in a class that adds the basic forward iterator-support (wrapping in a class also means not having naked pointers floating around, which I'd call a benefit in the name of developer sanity).