std::tuple member by member comparison fails - c++

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.

Related

How to use template function for implicit conversion

Very simplified example (nevermind what the class A and operators are doing, it's just for example):
#include <iostream>
using namespace std;
template <bool is_signed>
class A {
public:
// implicit conversion from int
A(int a) : a_{is_signed ? -a : a}
{}
int a_;
};
bool operator==(A<true> lhs, A<true> rhs) {
return lhs.a_ == rhs.a_;
}
bool operator==(A<false> lhs, A<false> rhs) {
return lhs.a_ == rhs.a_;
}
int main() {
A<true> a1{123};
A<false> a2{123};
cout << (a1 == 123) << endl;
cout << (a2 == 123) << endl;
return 0;
}
This works.
But if I replace two operator=='s (with same body) with template:
template <bool is_signed>
bool operator==(A<is_signed> lhs, A<is_signed> rhs) {
return lhs.a_ == rhs.a_;
}
, its compilation produces errors:
prog.cpp: In function ‘int main()’:
prog.cpp:31:14: error: no match for ‘operator==’ (operand types are ‘A<true>’ and ‘int’)
cout << (a1 == 123) << endl;
~~~^~~~~~
prog.cpp:23:6: note: candidate: ‘template<bool is_signed> bool operator==(A<is_signed>, A<is_signed>)’
bool operator==(A<is_signed> lhs, A<is_signed> rhs) {
^~~~~~~~
prog.cpp:23:6: note: template argument deduction/substitution failed:
prog.cpp:31:17: note: mismatched types ‘A<is_signed>’ and ‘int’
cout << (a1 == 123) << endl;
^~~
Is it possible to use template here? Can I use C++17 user-defined template deduction guides somehow? Or anything else?
Implicit conversions are not considered in template argument deduction, which causes the deduction for is_signed fails on the 2nd function argument.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
If you always use the operator== in the style like a1 == 123, i.e. an A<is_signed> is always used as the 1st operand, you can exclude the 2nd function parameter from the deduction. e.g.
template <bool is_signed>
bool operator==(A<is_signed> lhs, std::type_identity_t<A<is_signed>> rhs) {
return lhs.a_ == rhs.a_;
}
LIVE
PS: std::type_identity is supported since C++20; even it's not hard to implement one.
Another alternative is friend function, so the function is not template, but use the template argument:
template <bool is_signed>
class A {
public:
// implicit conversion from int
A(int a) : a_{is_signed ? -a : a}
{}
int a_;
friend bool operator==(const A& lhs, const A& rhs) {
return lhs.a_ == rhs.a_;
}
};
Demo
Template argument deduction does not take implicit conversions into account. You need again two overloaded comparison operators.
template <bool is_signed>
bool operator==(A<is_signed> lhs, A<is_signed> rhs) {
return lhs.a_ == rhs.a_;
}
template <bool is_signed>
bool operator==(A<is_signed> lhs, int rhs) {
return lhs == A<is_signed>(rhs);
}

Comparison on std::tuple with multiple empty members doesn't compile on GCC

When there are multiple structures with no members in a tuple, trying to compile a comparison of tuples on GCC results in following error:
<source>: In function 'bool foo()':
<source>:120:16: error: request for member 'operator<' is ambiguous
120 | return a < b;
| ^
<source>:46:10: note: candidates are: 'bool N::operator<(const N&) const'
46 | bool operator<(const N &) const noexcept {
| ^~~~~~~~
<source>:46:10: note: 'bool N::operator<(const N&) const'
Compiler returned: 1
Strangely enough, when I tie the same types I use in the tuple, it works as expected. One empty structure is also OK, two with different types are not.
Compilation with clang or msvc passes and yields expected results.
Is this correct behaviour, or is it a GCC/libstdc++ bug?
Demo
(Try it, uncomment the desired testcase first)
#include <tuple>
struct A {
int value;
A(int value) : value(value) {}
bool operator==(const A &other) const noexcept {
return value == other.value;
}
bool operator!=(const A &other) const noexcept {
return value != other.value;
}
bool operator<(const A &other) const noexcept {
return value < other.value;
}
};
struct N {
bool operator==(const N &) const noexcept {
return true;
}
bool operator!=(const N &) const noexcept {
return false;
}
bool operator<(const N &) const noexcept {
return false;
}
};
struct M {
bool operator==(const M &) const noexcept {
return true;
}
bool operator!=(const M &) const noexcept {
return false;
}
bool operator<(const M &) const noexcept {
return false;
}
};
using AAKey = std::tuple<A, A>;
using ANAKey = std::tuple<A, N, A>;
using ANANKey = std::tuple<A, N, A, N>;
using ANAMKey = std::tuple<A, N, A, M>;
using NKey = std::tuple<N>;
using NNKey = std::tuple<N, N>;
using NMKey = std::tuple<N, M>;
bool foo() {
/* Works
AAKey a{0, 1};
AAKey b{0, 0};
//*/
/* Works
ANAKey a{0, N{}, 1};
ANAKey b{0, N{}, 0};
//*/
/* Fails
ANANKey a{0, N{}, 0, N{}};
ANANKey b{0, N{}, 1, N{}};
//*/
/* Fails
ANAMKey a{0, N{}, 0, M{}};
ANAMKey b{0, N{}, 1, M{}};
//*/
/* Works
NKey a{N{}};
NKey b{N{}};
//*/
/* Fails
NNKey a{N{}, N{}};
NNKey b{N{}, N{}};
//*/
/* Fails
NMKey a{N{}, M{}};
NMKey b{N{}, M{}};
//*/
// Tying ANANKey into tuple:
/* Works
A ax1{0}, ay1{0}, ax2{0}, ay2{1};
N nx1, ny1, nx2, ny2;
auto a = std::tie(ax1, nx1, ax2, nx2);
auto b = std::tie(ay1, ny1, ay2, ny2);
//*/
return a < b;
}
EDIT
Extenal operator overloads actually work (thanks to #Turtlefight):
#include <tuple>
struct O {
friend bool operator==(const O &, const O &) noexcept {
return true;
}
friend bool operator!=(const O &, const O &) noexcept {
return false;
}
friend bool operator<(const O &, const O &) noexcept {
return false;
}
};
using OOKey = std::tuple<O, O>;
bool foo() {
OOKey a{O{}, O{}};
OOKey b{O{}, O{}};
return a < b;
}
This is clearly a bug, in the sense that the standard doesn't give permission for the comparison to not work.
The cause is more interesting. libstdc++'s tuple is EBO'd in such a way that tuple<N> is actually derived from N if N is an empty class. When you do a < b we need to do a nonmember and a member lookup for operator<. The nonmember lookup finds tuple's operator< as expected.
This member lookup, however, finds operator< in two different bases, so by [class.member.lookup]p6-7 this lookup produces an invalid set and is ill-formed on the spot.
Clang seems to accept this code by suppressing the error in the member lookup, but I'm not currently seeing anything in the standard that permits this behavior.

How to resolve an apply_visitor within another static_visitor in boost::variant? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I get a compiler regarding apply_visitor within the current operator.
I tested all the apply_visitor outside of that method and it works perfectly fine. Just when trying to use it within that method it runs into a bunch of issues.
The error message is confusing me.
So, Why is trying to use apply_visitor for a different visitor is running into problems within the current visitor?
Here the Snippet of the code that have give me the errors:
typedef boost::make_recursive_variant<string, int, vector<boost::recursive_variant_ > >::type ObjectE;
class ReturnType : public boost::static_visitor<string> {
public:
string operator()(string &s) { return "string"; }
string operator()(int i) {return "int";}
string operator()(std::vector<ObjectE> const &v) {return "vector";}
};
class ReturnVal : public boost::static_visitor<string> {
public:
string operator()(string &s) { return s; }
string operator()(int i) {return to_string(i);}
string operator()(std::vector<ObjectE> const &v) {return "vector";}
};
class ReturnV : public boost::static_visitor<vector<ObjectE>> {
public:
vector<ObjectE> operator()(string &s){ return {};}
vector<ObjectE> operator()(int i) {return {};}
vector<ObjectE> operator()(vector<ObjectE> const &v) {return v;}
};
struct create_Str {
using result_type = void;
std::ostream & out;
template <typename T> void call(T const &v) { return operator()(v);}
template <typename... Ts> void operator()(boost::variant<Ts...> const &v) {
return boost::apply_visitor(*this, v);
}
void operator()(int i) { out << i; }
void operator()(std::string const &s) { out << s; }
template <typename... Ts> void operator()(std::vector<Ts...> const &v) {
string name = boost::apply_visitor(ReturnVal(), v[0]);
if (v.size() == 3 && isOp(name)) {
}
else if (v.size() == 3 && isListCons(name)) {
call(v[1]);
ObjectE tail = v[2];
for (;;) {
if ("[]" == boost::get<string>(tail) || "nil" == boost::get<string>(tail)) {
break;
}
if ( !(boost::apply_visitor(ReturnType(), tail) == "vector")) {
}
vector<ObjectE> list = boost::apply_visitor(ReturnV(), v[2]);;
if (!(list.size() == 3 && isListCons(boost::apply_visitor(ReturnVal(), list[0])))) {
}
}
}
}
};
At first, I thought maybe
vector<ObjectE> list = v[2];
was the issue. So I created a visitor who will return a vector.
However, it seem that most of the errors are :
error: no match for call to '(const ReturnVal) (std::__cxx11::basic_string<char>&)'
which I'm not sure what it mean.
Can someone explain to me what the issue is?
Hah. Look back at my older answer to you:
Calls like
boost::apply_visitor(ReturnType(), tail)
call pass the visitor as a temporary, so it can only bind to a const&. This means that all call-operator overloads required must be const-qualified, because otherwise they won't apply, as you're being told:
Live On Coliru
#include <boost/variant.hpp>
struct Ok : boost::static_visitor<> {
void operator()(std::string const&) const {}
void operator()(int) const {}
};
struct NotWorkingWithTemporaries : boost::static_visitor<> {
void operator()(std::string const&) const {}
void operator()(int) {} // whoops, missing const-qualifier
};
int main() {
boost::variant<std::string, int> v;
boost::apply_visitor(Ok{}, v);
//boost::apply_visitor(NotWorkingWithTemporaries{}, v); // COMPILE ERROR
// however, this works:
NotWorkingWithTemporaries not_a_temporary;
boost::apply_visitor(not_a_temporary, v);
}
Compiles. Ucommenting the line commented with // COMPILE RROR gives:
Live On Coliru
In file included from /usr/local/include/boost/variant.hpp:17:0,
from main.cpp:1:
/usr/local/include/boost/variant/variant.hpp: In instantiation of 'boo...
/usr/local/include/boost/variant/detail/visitation_impl.hpp:114:9: r...
/usr/local/include/boost/variant/detail/visitation_impl.hpp:154:41: ...
/usr/local/include/boost/variant/detail/visitation_impl.hpp:238:5: r...
/usr/local/include/boost/variant/variant.hpp:2390:48: required from ...
/usr/local/include/boost/variant/variant.hpp:2404:43: required from ...
/usr/local/include/boost/variant/variant.hpp:2429:52: required from ...
/usr/local/include/boost/variant/detail/apply_visitor_unary.hpp:84:43:...
main.cpp:17:56: required from here
/usr/local/include/boost/variant/variant.hpp:1046:24: error: no match ...
return visitor_(operand);
~~~~~~~~^~~~~~~~~
main.cpp:9:10: note: candidate: void NotWorkingWithTemporaries::operat...
void operator()(std::string const&) const {}
^~~~~~~~
main.cpp:9:10: note: no known conversion for argument 1 from 'int' t...
main.cpp:10:10: note: candidate: void NotWorkingWithTemporaries::opera...
void operator()(int) {} // whoops, missing const-qualifier
^~~~~~~~
main.cpp:10:10: note: passing 'const NotWorkingWithTemporaries*' as ...
In file included from /usr/local/include/boost/variant.hpp:17:0,
from main.cpp:1:
/usr/local/include/boost/variant/variant.hpp:1046:32: error: return-st...
return visitor_(operand);
Skip the required from chain, scan for error: first and then notice the note: that says:
main.cpp:10:10: note: candidate: void NotWorkingWithTemporaries::operator()(int) <near match>
void operator()(int) {} // whoops, missing const-qualifier
^~~~~~~~
main.cpp:10:10: note: passing 'const NotWorkingWithTemporaries*' as 'this' argument discards qualifiers
It's pretty clear once you look for it: passing 'const NotWorkingWithTemporaries*' as 'this' argument discards qualifiers. The only thing you need to know is const is known as a qualifier¹.
¹ const, volatile and rvalue-ref (&&)
OTHER REMARKS
Trying to get the rest of your non-self-contained question code to compile reveals similar issues with the std::string overloads:
std::string operator()(std::string const & /*s*/) const { return "std::string"; }
(note std::string const& instead of std::string&).
Other than that... it look as though you're abusing vectors to represent Expressions. Why not make it strong typed and a lot less error-prone?
See e.g. Building a Custom Expression Tree in Spirit:Qi (Without Utree or Boost::Variant)

Call super operator== from a vector inherited class in C++

I'm trying to implement the compare operator of a class that inherits from vector.
I want it to compare first its own new attributes and then use the inherited operator from vector. This is an example:
struct A : vector<int> {
int a;
bool operator==(const A& other) {
return a == other.a && vector::operator==(other);
}
}
But I'm getting this error:
no member named 'operator==' in 'std::__1::vector<int, std::__1::allocator<int> >'
Same result with other classes from the STL, but it works well if I inherit from another class of my own.
This is the implementation of vector that I'm using:
inline _LIBCPP_INLINE_VISIBILITY
bool
operator==(const vector<_Tp, _Allocator>& __x, const vector<_Tp, _Allocator>& __y)
{
const typename vector<_Tp, _Allocator>::size_type __sz = __x.size();
return __sz == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
}
What I'm doing wrong?
vector's equality operator is a non-member function, which means you can't call it like that. You would be better off doing something like:
struct A : std::vector<int> {
int a;
bool operator==(const A& other) {
vector const& self = *this;
return a == other.a && self == other;
}
};
However, I wouldn't recommend inheriting from a standard container. Instead, you should have a std::vector<int> data member (composition over inheritance).

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).