Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking if iterator is not assigned to item when there is no specific container

Tags:

c++

c++11

vector

I want to search for a value in two std::vector's. If it was found in one of them I want to return its iterator. If not, I want to return some value which indicates that it was not found.

In normal situation where only one std::vector is involved, I would return std::vector::end. What should I do in this situation?

Normal situation:

auto find_ten=[](const std::vector<int>& v){
    return std::find(v.cbegin(),v.cend(),10);
}

My situation:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
             //What should I return here??
        }
        return it2;
    }
    return it1;
}

I want to return something that I can check it later to know that the number 10 was not found in any of them.

like image 586
Humam Helfawi Avatar asked Aug 06 '16 18:08

Humam Helfawi


4 Answers

Since C++14 you are allowed to compare value-initialized iterators if they meet the ForwardIterator category or stronger (see [forward.iterators] paragraph 2). A value-initialized iterator is the equivalent of a null pointer, so you can use:

    if(it2==v2.cend()){
         return std::vector<int>::iterator{};
    }

And then the caller can do:

std::vector<int>::iterator not_found{};
auto find_ten = ...
if (find_ten != not_found)
{
  ...
}
like image 118
Jonathan Wakely Avatar answered Nov 06 '22 06:11

Jonathan Wakely


auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);        
        return it2; // whether it's end or not
    }
    return it1;
}

when using, just test (rval != v2.end()) (rval is the returned iterator). I know, that's not very symmetric.

OR

Pass a boolean as out parameter

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2, bool &found){
    found = true; // suppose we will find it

    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);  
        found = it2 != v2.end();     // false if not in v1 or in v2
        return it2; // whether it's end or not
    }

    return it1;
}
like image 28
Jean-François Fabre Avatar answered Nov 06 '22 06:11

Jean-François Fabre


You could simply return the right-most vector's end iterator, this makes logical sense:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        return std::find(v2.cbegin(),v2.cend(),10);
    }
    return it1;
}

auto it = find_ten(v1, v2);
if (it == v2.end()) // no luck

Or you could take an iterator argument:

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2, std::vector<int>::iterator endit){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
             return endit;
        }
        return it2;
    }
    return it1;
}

These both, of course, depend on your use case: if you're going to use the returned iterator for more than just direct access to an element, you'll need to consider a different approach:

--- EDIT ---

If you need to use the iterator beyond just direct access to the offending element, you could use std::reference_wrapper to let you return a reference to the offending vector (or you could just return a pointer).

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

using find_ten_t = std::pair<std::reference_wrapper<const std::vector<int>>, std::vector<int>::const_iterator>;

auto find_ten = [](const std::vector<int>& v1, const std::vector<int>& v2) -> find_ten_t {
    auto it1 = std::find(v1.cbegin(), v1.cend(), 10);
    if (it1 != v1.cend()) {
        return std::make_pair(std::ref(v1), it1);
    }
    auto it2 = std::find(v2.cbegin(), v2.cend(), 10);
    return std::make_pair(std::cref(v2), it2);
};

int main() {
    std::vector<int> v1{ 1, 2, 3 };
    std::vector<int> v2{ 3, 4, 10 };
    auto r = find_ten(v1, v2);
    std::cout << "r.first[0] = " << r.first.get()[0] << "\n";
}

Live demo: http://ideone.com/cNLJKg

like image 1
kfsone Avatar answered Nov 06 '22 06:11

kfsone


Not really brilliant, I suppose, but... I propose returning a std::pair where the first element is an int (0 for "not found", 1 for "found in first" and 2 for "found in second") and the second is the iterator.

Something like

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
        auto it2=std::find(v2.cbegin(),v2.cend(),10);
        if(it2==v2.cend()){
            return std::make_pair(0, v1.cend()); // or make_pair(0, v2.cend())
        }
        return std::make_pair(2, it2);
    }
    return std::make_pair(1, it1);
};

Or better: you could return a pair of iterator where the second is the cend() of the corresponding vector; something like

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto it1=std::find(v1.cbegin(),v1.cend(),10);
    if(it1==v1.cend()){
       return std::make_pair(std::find(v2.cbegin(),v2.cend(),10), v2.cend());
    }
    return std::make_pair(it1, v1.cend());
};

I think it's important to return the cend() correspindig iterator because I suppose you want to use the iterator pointing to 10 and you could iterate it.

Obvioulsy, If you're only interested in knowing if 10 is present in v1 or in v2, I suppose you should returning a bool: true for "found", false otherwise; something like

auto find_ten=[](const std::vector<int>& v1,const std::vector<int>& v2){
    auto ret = v1.cend() != std::find(v1.cbegin(),v1.cend(),10);
    if ( ret == false )
       ret = v2.cend() != std::find(v2.cbegin(),v2.cend(),10);
    return ret;
};

p.s.: sorry for my bad English.

like image 1
max66 Avatar answered Nov 06 '22 06:11

max66