0

Currently, I have a problem when trying to find a variable within a vector. I defined a vector, tests, set to contain my own structure Test. It contains two variables, one and two. Both are instances of Test.

Test isn't much, it only contains an int and char.

I defined a macro that finds wether or not an instance of an object is in a given vector or not.

When I attempt to compile my code, it results in this error:

In file included from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:71,
                 from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/char_traits.h:39,
                 from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/ios:40,
                 from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/ostream:38,
                 from /usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/iostream:39,
                 from test.cpp:1:
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/predefined_ops.h: In instantiation of 'bool __gnu_cxx::__ops::_Iter_equals_val<_Value>::operator()(_Iterator) [with _Iterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Value = const Test]':
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:1932:14:   required from '_RandomAccessIterator std::__find_if(_RandomAccessIterator, _RandomAccessIterator, _Predicate, std::random_access_iterator_tag) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const Test>]'
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algobase.h:1977:23:   required from '_Iterator std::__find_if(_Iterator, _Iterator, _Predicate) [with _Iterator = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Predicate = __gnu_cxx::__ops::_Iter_equals_val<const Test>]'
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/stl_algo.h:3902:28:   required from '_IIter std::find(_IIter, _IIter, const _Tp&) [with _IIter = __gnu_cxx::__normal_iterator<Test*, std::vector<Test> >; _Tp = Test]'
test.cpp:19:6:   required from here
/usr/local/Cellar/gcc/10.2.0/include/c++/10.2.0/bits/predefined_ops.h:268:17: error: no match for 'operator==' (operand types are 'Test' and 'const Test')
  268 |  { return *__it == _M_value; }
      |

What I find interesting is that the variable I am checking is considered constant, even though when I defined it, it wasn't.

Source code:

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

#define isin(one, two) (std::find(one.begin(), one.end(), two) != one.end())

struct Test {
    int one;
    char two;
};

int main() {
    Test one = { 6, 'n' };
    Test two = { 9, 'r' };

    std::vector<Test> tests = { one, two };

    if (isin(tests, one)) {
        std::cout << "one is in tests\n";
    } else {
        std::cout << "one is not in tests\n";
    }

    return 0;
}

Why is one considered constant? Did I accidentally do something wrong?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
llamaking136
  • 421
  • 5
  • 16
  • 2
    The error message is telling you that you have no `operator==`. Why you're using a macro for this is beyond me. – Retired Ninja Apr 23 '21 at 00:04
  • 1
    It's easier to understand the intent of a call like `if (isin(haystack, needle)) {..}` than `if (std::find(haystack.begin(), haystack.end(), needle) != haystack.end()) {...}`, but it would be much safer (and due to function-inlining, equally runtime-efficient) to implement that syntactic sugar by writing a function instead of a macro. – Jeremy Friesner Apr 23 '21 at 00:15
  • On the `const` Good style says start with the most restrictive and work your way out to least restrictive as options get eliminated. Since there is no good reason for an `==` operator to change either of the arguments, `const` is the right place to start. The canonical `==` operator looks something like `bool operator==(const TYPE & lhs, const TYPE & rhs)` – user4581301 Apr 23 '21 at 00:34

2 Answers2

5
error: no match for 'operator==' (operand types are 'Test' and 'const Test')

You don't have an equality operator defined for Test. You need to define operator== so it can know how to check if two instances are equal.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
2

The error message is telling you that there is no operator== defined that can compare a non-const Test object on the left to a const Test object on the right. The compiler does not generate a default operator== for you (though, this is actually addressed in C++20, though it is not automatic, you still have to request it explicitly).

The 2nd Test in the error message is const because that is how std::find() is taking it in:

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value )

std::find() iterates the specified range comparing value to each element in the range using operator==, similar to this:

template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value )
{
    while (first != last) {
        if (*first == value) { // <-- here
            return first;
        }
        ++first;
    }
    return last;
}

In this case, T is Test, so value is const Test &value. tests is a non-const std::vector<Test>, so its iterators refer to non-const Test elements. But the value being compared to them is a const Test.

You need to define an operator== for Test that can handle that comparison, eg:

struct Test {
    int one;
    char two;

    bool operator==(const Test &rhs) const {
        return one == rhs.one && two == rhs.two;
        /* alternatively:
        return std::tie(one, two) == std::tie(rhs.one, rhs.two);
        */
    }

    /* alternatively in C++20 onwards:
    bool operator==(const Test &) const = default;  
    */
};
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770