Can't compare two ranges - c++

Can't figure out why std::ranges::equal in the code below does not compile:
struct A { int x; };
using Map = std::map<int, A>;
void some_func()
{
std::vector<A> v{ {0}, {1}, {2}, {3} };
auto v2m = [](const A& a) { return std::pair<int, A>(a.x, a); };
const auto actual = std::ranges::single_view(v[2]) | std::views::transform(v2m);
Map expected{ {v[2].x, v[2]} };
//Does not compile.
bool equals = std::ranges::equal(actual, expected);
}
The compiler errors with MSVC are:
error C2672: 'operator __surrogate_func': no matching overloaded function found
error C7602: 'std::ranges::_Equal_fn::operator ()': the associated constraints are not satisfied

Problem 1: A isn't comparable, so you cannot compare it using std::ranges::equal with the default predicate. Solution:
struct A {
int x;
friend auto operator<=>(const A&, const A&) = default;
};
Problem 2: Your transform function produces std::pair<int, A> which doesn't match with the elements of map which are std::pair<const int, A>. Solution: use std::pair<const int, A> (or just Map::value_type so that there's less room for mistakes).

Related

C++ Reuse Lambda as Compare function in `std::priority_queue`

When working with std::priority_queue, I tried to clear the contents of the priority queue like this:
#include <iostream>
#include <queue>
#include <vector>
using std::cout;
using std::priority_queue;
using std::vector;
int main() {
const auto comp = [](int a, int b) {
return a > b;
};
auto a = priority_queue<int, vector<int>, decltype(comp)>(comp);
a.push(10);
a.push(9);
a.push(8);
// 3
cout << a.size() << '\n';
a = priority_queue<int, vector<int>, decltype(comp)>(comp);
// 0
cout << a.size() << '\n';
return 0;
}
When compiling with Clang, I got an error:
In file included from tmp.cpp:1:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/queue:455:39: error: no viable overloaded '='
{c = _VSTD::move(__q.c); comp = _VSTD::move(__q.comp); return *this;}
~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~
tmp.cpp:19:7: note: in instantiation of member function 'std::priority_queue<int, std::vector<int>, const (lambda at tmp.cpp:9:23)>::operator=' requested here
a = priority_queue<int, vector<int>, decltype(comp)>(comp);
^
tmp.cpp:9:23: note: candidate function (the implicit copy assignment operator) not viable: 'this' argument has type 'std::priority_queue<int, std::vector<int>, const (lambda at tmp.cpp:9:23)>::value_compare' (aka 'const (lambda at tmp.cpp:9:23)'), but method is not marked const
const auto comp = [](int a, int b) {
^
1 error generated.
Why would this give me an error? And is there any way to fix it? Thanks.
decltype(comp) is a const type, const auto comp, that makes the priority_queue member variable storing comp to be constant, thus can't be re-assigned.
You might want
priority_queue<int, vector<int>, remove_cv_t<decltype(comp)>>(comp);
Or
auto comp = [](int a, int b) {
return a > b;
};
The copy assignment operator for lambdas are implicitly deleted. The fix:
std::function<bool(int, int)> comp = [](int a, int b) {
return a > b;
};

Returning assignable value for std::map() using overloaded [] operator

I would like to insert data in my Matrix class using the [] operator. The argument of this operator, a std::array<int, 2>, is the key for the std::map.
template <typename T>
class Matrix
{
public:
Matrix(const int& rows, const int& cols)
: rows(rows), cols(cols)
{}
std::map<std::array<int, 2>, T>& operator[] (std::array<int, 2> x) {
return data[x];
}
private:
std::map<std::array<int, 2>, T> data;
const int rows;
const int cols;
};
int main()
{
Matrix<double> M(10, 10);
M[{10, 10}] = (double) 10;
return 0;
}
I am not able to return the reference to the map in a way that it is assignable, and keep getting the following error:
error: non-const lvalue reference to type 'std::map<std::array<int, 2>, double>' cannot bind
to a value of unrelated type 'std::__1::map<std::__1::array<int, 2>, double,
std::__1::less<std::__1::array<int, 2> >, std::__1::allocator<std::__1::pair<const std::__1::array<int, 2>,
double> > >::mapped_type' (aka 'double')
return data[x];
I got it working when returning a pointer to data[x], dereferencing this and assigning a value to that. This does not deliver neat code, and I think it can be done more easily. So what am I missing here?
If you have a map:
std::map<std::array<int, 2>, T>
then its key_type is std::array<int,2> and the mapped_type is T. operator[] takes a key and returns a reference to the corresponding value.
One correct way would be
T& operator[] (const std::array<int, 2>& x) {
return data[x];
}
Or if you actually want T as keys and the arrays as values you rather need a
std::map<T, std::array<int, 2>>
and the method could be
std::array<int,2>& operator[] (const T& x) {
return data[x];
}

std::tuple member by member comparison fails

I wanted to test this very interesting answer and came out with this minimal implementation:
class A
{
enum M { a };
std::tuple<int> members;
public:
A() { std::get<M::a>(members) = 0; }
A(int value) { std::get<M::a>(members) = value; }
A(const A & other) { members = other.members; }
int get() const { return std::get<M::a>(members); }
bool operator==(A & other) { return members == other.members; }
};
and a simple test:
int main() {
A x(42);
A y(x);
std::cout << (x==y) << std::endl;
return 0;
}
Everything's fine, until I define a simple struct B {}; and try to add an instance of it as a member. As soon as I write
std::tuple<int, B> members;
the operator== isn't ok anymore and I get this message from the compiler (gcc 5.4.1):
error: no match for ‘operator==’ (operand types are ‘std::__tuple_element_t<1ul, std::tuple<int, B> > {aka const B}’ and ‘std::__tuple_element_t<1ul, std::tuple<int, B> > {aka const B}’)
return bool(std::get<__i>(__t) == std::get<__i>(__u))
^
I tried providing an operator== to B:
struct B
{
bool operator==(const B &){ return true; }
};
and had an extra from compiler:
candidate: bool B::operator==(const B&) <near match>
bool operator==(const B &){ return true; }
^
Can anyone explain what's wrong with B struct? Is it lacking something, or else?
It's ultimately const correctness. You didn't const qualify B's (or A's, for that matter) comparison operator and its parameter consistently.
Since tuple's operator== accepts by a const reference, it cannot use your const-incorrect implementation. And overload resolution fails as a consequence.
Tidying up all of those const qualifiers resolves all the errors.

Creating map entry using a user-defined conversion

I'm got a map of key type std::pair<Foo,Bar> and value in which I would like to insert into a map by passing the pair std::pair<std::pair<Foo,Bar> , int> to the insert function like so (online source)
struct Foo{};
struct Bar{};
typedef std::pair<Foo,Bar> FooBar;
typedef std::pair<FooBar,int> FooBarPair;
typedef std::map<FooBar,int> FooBarMap;
struct FooBarData
{
operator const FooBarPair() const
{
return std::make_pair( std::make_pair( m_foo , m_bar ) , m_num );
}
private:
int m_num;
Foo m_foo;
Bar m_bar;
};
int main()
{
FooBarData fbd;
FooBarMap fbm;
fbm.insert( fbd );
}
clang error message
/usr/include/c++/v1/map:1041:9: note: candidate function not viable: no known conversion from 'FooBarData' to 'const value_type' (aka 'const pair<const key_type, mapped_type>') for 1st argument
insert(const value_type& __v) {return __tree_.__insert_unique(__v);}
^
/usr/include/c++/v1/map:1050:14: note: candidate function template not viable: requires 2 arguments, but 1 was provided
void insert(_InputIterator __f, _InputIterator __l)
^
/usr/include/c++/v1/map:1045:9: note: candidate function not viable: requires 2 arguments, but 1 was provided
insert(const_iterator __p, const value_type& __v)
Is there any fix along these lines?
Add missing operator <.
Also, remove const from return type of conversion operator.
Edit After your change in Q:
You have to change your conversion to
operator std::pair<const FooBar, int>() const
Else, it would require 2 user conversions, which is not allowed.
Demo
(At least sometimes), the map implementations are/were red black trees. For map insert to work, your key type needs to support comparison (operator <). I cannot see this requirement to be met for your empty structs and the pair you create from them.
So as a first sanity check, I would replace Foo and Bar structs with
typedef int Foo;
typedef int Bar;
in order to see if something else is wrong with your code.
If it still is not compiling, check if std::pair<> supports comparison.
Also, instead of typedef'ing the FooBarPair yourself, you can simply enjoy that the map template already did that for you. For example:
...
operator FooBarMap::value_type() {
...
}
Here a fixed version, which compiles:
#include <map>
struct Foo { int x; };
struct Bar {};
typedef std::pair<Foo, Bar> FooBar;
typedef std::map<FooBar, int> FooBarMap;
bool operator <(const FooBarMap::key_type&lhs, const FooBarMap::key_type& rhs)
{
return lhs.first.x < rhs.first.x;
}
struct FooBarData
{
operator FooBarMap::value_type() const
{
return FooBarMap::value_type(FooBarMap::key_type(m_foo, m_bar), m_num);
}
private:
int m_num;
Foo m_foo;
Bar m_bar;
};
int main()
{
FooBarData fbd;
FooBarMap fbm;
fbm.insert(fbd);
}

Equality operator (==) unsupported on map iterators for non-copyable maps

When I have a map of non-copyable objects, why can't I compare the iterators using ==? I would imagine it does not need to copy (or move) the actual objects when doing equality testing on the iterators.
#include <iostream>
#include <utility>
#include <map>
using namespace std;
class A {
private:
int i;
public:
A(int i) : i(i) {}
A(A&) = delete;
A(A&& a) : i(a.i) {}
~A() {}
A& operator=(A&) = delete;
bool operator==(const A& a) const { return i == a.i; }
};
int main() {
map<int, A> myMap;
map<int, A>::iterator it = myMap.find(1);
cout << (it == myMap.end()) << endl;
}
This example fails to compile, giving an error on the line with the cout.
g++ gives this error:
/usr/include/c++/4.8.2/bits/stl_pair.h: In instantiation of ‘struct std::pair<const int, A>’:
test2.cpp:24:27: required from here
/usr/include/c++/4.8.2/bits/stl_pair.h:127:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = A]’ declared to take const reference, but implicit declaration would take non-const
constexpr pair(const pair&) = default;
clang++ gives this error:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/bits/stl_pair.h:127:17: error: the parameter for this explicitly-defaulted copy constructor is const, but a member or base requires it to be non-const
constexpr pair(const pair&) = default;
^
test2.cpp:24:14: note: in instantiation of template class 'std::pair<const int, A>' requested here
cout << (it == myMap.end()) << endl;
However, it does work if a map<int, int> is used instead of a map<int, A>. Using a const map<int, A> with map<int, A>::const_iterator does not help.
I tried looking up the exact signature of map::iterator::operator== on cppreference, (map::iterator is a BidirectionalIterator, which is a ForwardIterator, which is an InputIterator) but the website is vague about the exact type signatures in the concepts.
The problem is with your deleted methods to make A noncopyable ...
#include <iostream>
#include <utility>
#include <map>
using namespace std;
class A {
private:
int i;
public:
A(int i) : i(i) {}
// A(A&) = delete; should be ...
A(const A&) = delete;
A(A&& a) : i(a.i) {}
~A() {}
// A& operator=(A&) = delete; should be ...
A& operator=(const A&) = delete;
bool operator==(const A& a) const { return i == a.i; }
};
int main() {
map<int, A> myMap;
map<int, A>::iterator it = myMap.find(1);
cout << (it == myMap.end()) << endl;
}
Verified this with gcc/g++ 4.6.3
Removing the "const" from the copy constructor and the assignment operator caused my compiler to give the same error which you received.
I would have to look up the details/references as to why those are declared that way, but the copy constructor and the assignment operator always take a const reference (make sense as the value to which your instance is being assigned should not be modified by the method).