Overload Resolution for comparisons of c++ compare objects - c++

I wanted to introduce the spaceship operator in our Code Base, but we are currently using global templated comparison operators like this:
template <typename L, typename R>
bool operator > (const L& l, const R& r) { return r < l; }
(operator < is usually a member)
When I now try and use operator <=> in a class, my templated operator gets chosen! Why?
In cpp reference the example implementation of the ordering operators are friends and should in every case be superior to a templated function.
Example: https://godbolt.org/z/r8qjK6q6f

std::strong_ordering has comparison operators for which one parameter is of unspecified type. It's not clear how they factor in overload resolution, although it is quite probable that they are intentionally the least viable function.
Comparison operators are defined between values of this type and literal ​0​. This supports the expressions a <=> b == 0 or a <=> b < 0 that can be used to convert the result of a three-way comparison operator to a boolean relationship; see std::is_eq, std::is_lt, etc.
These functions are not visible to ordinary unqualified or qualified lookup, and can only be found by argument-dependent lookup when std::strong_ordering is an associated class of the arguments.
The behavior of a program that attempts to compare a strong_ordering with anything other than the integer literal ​0​ is undefined.
Your comparison template is wildly too general. It is implying that anything is > comparable to anything else. Either remove it, or add a requires clause.
template <typename L, typename R>
requires requires (const L& l, const R& r) { r < l; }
bool operator > (const L& l, const R& r) { return r < l; }

Related

unexpected error in unordered_map iterator comparison

I have a simple map std::map<string, my_namespace::MyType>, I am using c++11 so I replaced it with unordered_map for performance reasons. I got the following error when comparing an iterator with end().
auto cit = str_map_.find(str);
if (cit != str_map_.end()) {
...
}
In instantiation of 'bool my_namespace::operator!=(const T1&, const T2&) [with T1 = std::__detail::_Node_iterator<std::pair<const std::__cxx11::basic_string, my_namespace::MyType, false, true>; T2 = std::__detail::_Node_iterator<std::pair\ <const std::__cxx11::basic_string, my_namespace::MyType, false, true>]': no matching function ...
I debugged it down to my rather creative comparison operators for my_namespace::MyType:
template <class T>
struct MyType {
T* mt_;
};
struct MyTempClass {
std::string mtc_;
static int Compare(MyType<MyTempClass> const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.mt_->mtc_.compare(rhs.mt_->mtc_);
}
static int Compare(std::string const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.compare(rhs.mt_->mtc_);
}
static int Compare(MyType<MyTempClass> const& lhs, std::string const& rhs) {
return lhs.mt_->mtc_.compare(rhs);
}
};
template <class T1, class T2>
bool operator !=(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator ==(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
static std::unordered_map<std::string, MyType<MyTempClass>> my_map;
But I am still puzzled why it did happen: the same code works fine with a plain map, and values type should not be involved in iterator comparisons?
You defined an operator!= overload that takes any type as an argument. That overload is in the same namespace as the type MyType. Therefore, it can potentially be found via ADL.
As the error message indicates, the std::unordered_map iterator used by the standard library is a class template specialization, specialized on the std::unordered_map template arguments. As such, when you compare iterators with !=, ADL is performed on the arguments and the namespaces searched by ADL also include the namespaces of type template arguments of the types of the arguments. Therefore, your operator!= overload in the namespace of MyType will also be found and participate in overload resolution.
Assuming you are using libstdc++ as standard library implementation based on the error message, you can have a look at it's implementation of the operator!= for hash table iterators and you will see that it uses a base class for these iterators, and defines the comparison operators for references to the base class objects.
As a consequence, the standard overload for the iterator comparison requires a derived-to-base reference conversion in its arguments, while your overload does not.
Therefore your overload is better and will be chosen to do the cit != str_map_.end() comparison. Your overload tries to pass the arguments MyTempClass::Compare which clearly doesn't work, because these functions don't expect std::unordered_map iterators.
The solution is not to overload operators for pairs of types that do not depend on user-defined types. Restrict your overloads to your own types:
template <class T1, class T2>
bool operator !=(MyType<T1> const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator !=(T1 const& lhs, MyType<T2> const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
(equivalently for operator==).
As far as I know, it is not forbidden to overload the operators for standard library type pairs, but I also don't think that the standard library is required to account for conflicts this generates as in your code.
With std::map the standard library implementation might have chosen a different way of implementing the iterator comparison, which made it a better fit in overload resolution or avoided that ADL finds your overload by not making the iterator a template specialized on the key/value type.

user defined data type in set in c++

In the first case, code is working fine but I am getting an error in the second code, the only difference is of "const" in the operator overloading of '<', I am not able to figure out why.
Code for both cases are below
1.
class first
{
public:
int y;
bool operator < (first t) const
{
return (y>t.y);
}
};
set<first> f;
2.
class first
{
public:
int y;
bool operator < (first t)
{
return (y>t.y);
}
};
set<first> f;
The default comparator for std::set is std::less<Key> and as we can see on std::less it defines:
constexpr bool operator()( const T& lhs, const T& rhs ) const;
Which accepts const arguments and returns lhs < rhs. This of course doesn't work if lhs < rhs is not valid for const arguments, as it is in your second case because bool operator < (first t) cannot be called.
Writing your own comparator which accepts non-const arguments doesn't seem to work either, so it looks it is a requirement of the ordered container, but further check with the standard is needed to confirm.
std::set, like most other containers in the standard library, uses the requirement Compare for its comparing function. And the Compare requirement enforces constness:
As with any BinaryPredicate, evaluation of that expression is not allowed to call non-const functions through the dereferenced iterators.

How to define boost::any operator ==

I want to define operator == for boost::any in my project. Since the arguments belong to the boost namespace, this is where argument-dependent lookup will search for it. So, the signature is:
namespace boost
{
bool operator == (const boost::any &, const boost::any &);
}
However, this generates ambiguous overload errors whenever I include a boost library that compares enums for equality, such as thread/locks.hpp — the compiler sees no reason to prefer converting the enums to int and using the built-in comparison instead of converting them to boost::any and using mine.
I can hack around this by also including any such libraries in the same file as my comparator and defining custom operators for comparing boost's enums. But there got to be a better way, right?
namespace boost {
template<class T,
typename std::enable_if<std::is_same<T, any>{}, bool>::type =true
>
bool operator == (const T& lhs, const T& rhs){
return any_equal(lhs, rhs);
}
}

Standard(or best looking) SFINAE implementation for stream opearator overload

I have the following code
template<typename S, typename T, bool h = is_class< decltype(T::children)>::value >
S& operator<<(S& s, const T& t )
{
return s;
}
that will overload the operator<< when a class have a member variable named children.
What is the standard way or do there exist a better looking way for making the function fail when is_class< decltype(T::children)> fails.
I cannot use the return type or an argument because this is operator overloading.

Overloading [] but its result isn't resolving before interacting with other operators (c++)

I've got a data object that I'm trying to get all of the operators working with. It's one chunk of data with variable ptrs into it, and has any number of different types and sizes and whatnot. Types are handled with enums and templates and switch statements. So for each x, d[x] is one type, with any number of them and they can be vectors. so d[x][y] and d[x][y][z]. I made an internal helper object to help with this. So I have [] overloaded to do things like this, and it'll return the correct type fine: (gcc 4.6.1)
[Edit: I have the same trouble with d(x,y,z)--the problem isn't the [] operator]
int i = d[0][3][5];
I'm overloading T() in this helper object.
template <class T>
data::helper::operator T (); // switch(x)...return different types
data::helper data::operator [] (int i); // recurse, return helper(t, d, x, i, j);
So I'll just return this object, which resolves it's type at that point (switch with cases tied to t->get< char>(d, x, i, j), etc.). So the problem is, if I want to do anything like this
int i = d[0][1] + d[4][2];
if (d[5][1] != d[3][0]) ...
then I ended up having to overload every operator to take in this temporary array helper object. And now I'm running into having to make a temporary value in there sometimes for some of the operators, which is a pain.
Basically, I feel like I need the operator T() to resolve first, before the compiler tries to take two of these and add them.
I have to do this anyway for the = and +=, etc. operators, but I'd like to delete these jazillion macros helping me define all of these other operators.
Also, I feel like if I could overload the lvalue operator somehow, I could not worry about the = operator. Maybe that and &() (which right now just returns a templated ptr). ...? Or actually, this is more what I mean, at least for d[] = something, but I haven't got this to work. I'm not sure how to convert a ptr of any type to this return value.
data::helper & data::operator [] (int i);
I have most of this working but it's a lot of code, and I think I'm going to have to add an extra if statement to every access to do the temp stuff, which I don't want to do. So what did I miss?
Edit: using d(x,i,j) is the same as d[x][i][j]. I'm pretty sure I'm doing at least the beginning part of what's being used in the link n.m. posted. The problem is resolving that last helper object into its data before it's used in a statement. Somehow the compiler wants an operator that accepts the helper object even though it knows how to resolve it when it's alone... I think. Been a couple days into overloading every operator so I forget all the details. :)
But the main problem now is with stuff like this:
helper operator + (helper & l, helper & r)
I would like to define the following but it's not getting used--then I think my problems might be solved. similar story for unary ops ~, -, and postfix ++, --.
template <class T> T operator + (helper & l, helper & r)
But all of this is just because there's something off about my T(), I think. Most of this is new to me, so I bet I'm missing something.
The practical way to do this kind of thing is with expression templates.
I'd change your return values from operator[] to an expression template even.
This will use C++11 features, because it makes it shorter.
enum class ExpressionType { Index, Addition };
template< ExpressionType Op, typename LHS, typename RHS >
struct Expression {
LHS lhs;
RHS rhs;
template<typename T>
operator T();
};
// to separate out the evaluation code:
template< typename T, ExpressionType Op, typename LHS, typename RHS >
struct Evaluate {
T operator()( Expression<Op, LHS, RHS> exp ) const;
};
template< ExpressionType Op, typename LHS, typename RHS >
template<typename T>
Expression<Op,LHS,RHS>::operator T() {
return Evaluate<T,Op,LHS,RHS>()( std::move(*this) );
}
// further specializations needed:
template< typename T, typename RHS >
struct Evaluate< T, ExpressionType::Index, data, RHS > {
T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
// we just assume RHS can be treated like an integer. If it cannot,
// we fail to compile. We can improve this with SFINAE elsewhere...
return exp.lhs.get_nth(exp.rhs);
}
};
template< typename T, typename LHS, typename RHS >
struct Evaluate< T, ExpressionType::Addition, LHS, RHS > {
T operator()( Expression<Op, ExpressionType::Index, data, RHS> exp ) const {
// code with all of LHS, RHS and T visible!
}
};
template<typename E>
struct is_expression : std::false_type {};
template<ExpressionType Op, typename LHS, typename RHS>
struct is_expression<Expression<Op,LHS,RHS> : std::true_type {};
template<ExpressionType Op, typename LHS, typename RHS>
Expression<Op, LHS, RHS> make_expression( LHS&& lhs, RHS&& rhs ) {
return { std::forward<LHS>(lhs), std::forward<RHS>(rhs) };
}
// here is why I want to start out with returning an expression. This SFINAE test
// is extremely easy because of that -- we overload operator+ on any two types, so long
// as one of them is an Expression!
template<typename LHS, typename RHS, typename=typename std::enable_if<is_expression<LHS>::value || is_expression<RHS>::value >::type>
ExpressionType<ExpressionType::Addition, LHS, RHS> operator+( LHS&& lhs, RHS&& rhs )
{
return make_expression<ExpressionType::Addition>(std::forward<LHS>(lhs), std::forward<RHS>(rhs) );
}
so the idea is, we build at compile time a tree of templates that represent the order in which various expressions are evaluated by the compiler.
When we finally cast it to a concrete type T, only then do we start the evaluation work.
This avoids having to create any temporaries, but does mean we have to do a lot of template mojo in order to get things up and running. The above is a sketch of such an template expression tree generator.
To see a complete implementation of a simple case, here is a link to wikipedia's article on the subject, where a full blown expression tree system is built up to do std::vector vector processing without temporaries.