!= auto generated from == in C++20? - c++

Is this standard behavior in C++20? I couldn't find anything about it in cppreference.
I've just tried both on Clang and on Visual Studio and it works and it doesn't give me any sort of error or warning. I also checked with the debugger to see if operator== was being called and it was! Does C++20 now allows for the automatic generation of operator!= when operator== is present? Does it default to a sane !(a == b)? If that's so, then that's awesome for C++!

!= auto generated from == in C++20?
Is this standard behavior in C++20?
Yes. operator!= is auto generated from operator== in C++20.
Furthermore, all four relational operators are generated if you define operator<=>, and all of the comparison operators are generated if you define operator<=> as defaulted.
What you want to do in most cases:
struct example
{
std::string a;
int b;
auto operator<=>(const example&) const = default;
};

What you can do in older C++ version is to use the CRTP (Curriously Recuring Template principle).
The idea is to have a template base, and the template parameter is the Derived class :
template <typename Derived>
class Comparable {
public:
friend constexpr auto operator!=(const Derived &a, const Derived &b) noexcept { return !(a == b); }
friend constexpr auto operator<=(const Derived &a, const Derived &b) noexcept { return !(b < a); }
friend constexpr auto operator>(const Derived &a, const Derived &b) noexcept { return b < a; }
friend constexpr auto operator>=(const Derived &a, const Derived &b) noexcept { return !(a < b); }
};
And so, you can use it like this :
struct Example : Comparable<Example> {
friend bool operator==(const Example &a, const Example &b);
friend bool operator<(const Example &a, const Example &b);
};
If you declare only the == operator, you will have the != generated automatically, and if you provide both the < and == all operators will be defined :)

Related

Problems overloading operator== of template sub class

I've problems overloading operator==, different compiler errors using VC++(2015) and g++ 5.4.0 (--std==c++14). Here's the code (this is just an extract of a more complex situation in my real code base):
#include <vector>
template<typename T>
struct A {
struct B {
std::vector<T> _elements;
// Internal cmp op.
bool operator==(const B &other) {
return _elements == other._elements;
}
};
std::vector<B> _entries;
};
// External cmp op.
template<typename T>
inline bool operator==(typename const A<T>::B &l, typename const A<T>::B & r) {
return l._elements == r._elements;
}
int main() {
A<int>::B b0, b1;
b0.operator==(b1); // a
operator==<int>(b0, b1); // b
b0 == b1; // c
std::vector<A<int>::B> v0, v1;
std::equal(v0.begin(), v0.end(), v1.begin()); // d
v0 == v1; // e
return 0;
}
I do not add the error messages, because I have the german version of VC++ and the g++ errors span over many lines.
VC++ gives an error on (e). I don't understand why, because vector<>::operator== seems to call std::equal internally and (d) compiles fine. Why does this fail?
g++ fails to accept my external operator==(), so totally fails to compile this short code. I have no idea how to write an external operator==() for A<T>::B that works with both compilers.
I haven't tried clang yet.
Many thanks.
There were two errors in your program:
// Internal cmp op.
bool operator==(const B &other) const {
///// <- here
return _elements == other._elements;
}
should be a const member and the keyword const cannot appear just behind the typename:
// External cmp op.
template<typename T>
inline bool operator==(typename A<T>::B const& lhs, typename A<T>::B const& rhs)
///// <- here -> ////
{
return lhs._elements == rhs._elements;
}
Live Example
Note that the placement of const is usually fairly liberal in C++, e.g. you can write both const typename A<T>::B & lhs and typename A<T>::B const& lhs, but the form you chose, typename const A<T>::B & lhs is not allowed.
Also note that you want to write either a member operator== or a non-member operator==, but never both. In your case, because T is not deducible in typename A<T>::B, you have to write the ugly operator==<int>(b0, b1) to select the non-member.
I would remove the non-member template operator== and add to your class template A<T> a non-member
bool operator==(const A &other) const {
return _entries == other._entries;
}
so that you can also compare objects of A<int>. Note that this will call the standard library operator== for std::vector which in turn will call your operator== for B.

Operators for non-primitive boxed types

I have the following class definition:
template <typename T>
class MyBox {
public:
MyBox(T value) { _value = value; }
operator T() const { return _value; }
private:
T _value;
};
typedef MyBox<int> MyInt;
typedef MyBox<std::string> MyString;
When I try to use operators on my typedefs like this
bool first = MyInt(1) == MyInt(1); // works
bool second = std::string(MyString("a")) == std::string(MyString("a")); //works
bool third = MyString("a") == MyString("a"); // does not compile
the compiler complains about the third comparison
no operator "==" matches these operands. operand types are: MyString == MyString
and this happens with any other non-primitve boxing (e.g. MyBox<float> works but MyBox<std::map<int,int> > not. Why is that so?
This is especially unclear to me because for the first and second comparison the operator T() is used - why can't that be done automatically for MyString as well?
UPDATE: Is there a simple solution to this other than providing the specific operators for each non-primitive template? And what to do with MyString("a") == std::string("a")?
The reasons on why it works for built-in types, but does't work for custom types is answered in the following SO quesiton: using user-defined conversions with implicit conversions in comparisons. In short, this is because type conversion does not happen for template-deduced types. And while built-in operator== for int is not a template (and thus can be found using type conversion when MyBox<int> is used), operator== for std::string is a template.
However, the question mentioned above doesn't have details on how to solve this problem. Here is how: add following free functions
template<class T>
bool operator==(const MyBox<T>& lhs, const MyBox<T>& rhs) {
return static_cast<const T&>(lhs) == static_cast<const T&>(rhs);
}
template<class T>
bool operator==(const MyBox<T>& lhs, const T& rhs) {
return static_cast<const T&>(lhs) == rhs;
}
template<class T>
bool operator==(const T& lhs, const MyBox<T>& rhs) {
return lhs == static_cast<const T&>(rhs);
}

Why standard containers use function templates instead of non-template Koenig operators

This question is inspired by Issue with std::reference_wrapper. Let' say, for example, operator< for std::vector. It's defined as a function template as
template< class T, class Alloc >
bool operator<( const vector<T,Alloc>& lhs,
const vector<T,Alloc>& rhs );
As a result, implicit conversion of function argument to the type of the corresponding function parameter is denied (basically because of its template nature). This greatly reduces the usefulness and convenience of std::reference_wrapper. For example, you cannot use std::sort on std::vector<std::reference_wrapper<std::vector<int>>>.
On the other hand, all the problems are solved only if operator< is defined as a non-template Koenig operator like
template <...>
class vector ... {
friend bool operator<(const vector& a, const vector& b) {...}
};
I'm wondering why the standard library has adopted the former approach instead of this?
Consider this code (A.h):
template <class T>
class A {
public:
T m_x;
friend bool operator<(const A & lhs, const A & rhs) {
return lhs.m_x < rhs.m_x;
}
};
And main.cpp:
#include "A.h"
namespace buddy {
bool operator<(const A<double> & lhs, const A<double> &rhs) {
return lhs.m_x > rhs.m_x;
};
}
using namespace buddy;
int main(int argc, char ** argv) {
A<double> a1;
A<double> a2;
a1 < a2;
return 0;
}
This code does not compile:
main.cpp:14:5: error: ambiguous overload for ‘operator<’ (operand types are ‘A’ and ‘A’)
a1 < a2;
The reason of course is that both of the operator<'s are exact matches. On the other hand, if we change the first operator< to (defined outside the class):
template <class T>
bool operator<(const A<T> & lhs, const A<T> & rhs) {
return lhs.m_x < rhs.m_x;
}
The compiler stops complaining: it's now a competition between an exact match, and a function template, so the exact match is used.
If operator< was defined in the fashion that you're suggesting, there would be no reasonable way for users of std::vector to redefine the behavior of operator<, short of specializing std::vector themselves, which is a lot more work.
In conclusion, the standard writers elected to make it easier to overload operator<, than to provide an operator< that might be more useful in certain situations. I think they made the right choice.

Do I need to manually declare >= and <= operators?

If I already have operator > and operator < defined (and operator ==), do I need to define operator >= and operator <=, or will the compiler declare them for me if I intentionally don't declare them?
Also, if I have operator == defined, will the compiler declare operator != for me?
No, the Compiler won't declare/define any of the operators you did not define manually. However, Boost.Operators might be to your liking - it does exactly what you want the compiler to do.
The compiler won't do anything itself for you here, but it's
relatively simple to generate automatically by inheriting from
an appropriate class, something like:
template< typename DerivedType >
class ComparisonOperators
{
public:
friend bool operator!=(
DerivedType const& lhs,
DerivedType const& rhs )
{
return !(lhs == rhs);
}
friend bool operator<=(
DerivedType const& lhs,
DerivedType const& rhs )
{
return !(rhs < lhs);
}
friend bool operator>(
DerivedType const& lhs,
DerivedType const& rhs )
{
return rhs < lhs;
}
friend bool operator>=(
DerivedType const& lhs,
DerivedType const& rhs )
{
return !(lhs < rhs);
}
protected:
~ComparisonOperators() {}
} ;
Define < and == in your class, and derive from this, and
you'll get all of the operators:
class MyClass : public ComparisonOperators<MyClass>
{
// ...
public:
bool operator==( MyClass const& other ) const;
bool operator<( MyClass const& other ) const;
// ...
};
Just a note: I've manually simplified the version I actual use,
which defines == and < as well, looks for the member
functions compare and isEqual, and uses compare for ==
and != when there is no isEqual. I don't think I've
introduced any errors, but you never know.
There are already some good answers here using boost and inheritance. But as someone noted - using inheritance for operator creation seems... wrong.
I know #defines are "taboo" in C++, but still that's what I use here.
I have a #define in my general utility include that goes like this:
#define CREATE_COMPARITORS(name) \
inline bool operator>(const name &o){return o<*this;} \
inline bool operator<=(const name &o){return not (o<*this);} \
inline bool operator>=(const name &o){return not (*this<o);} \
inline bool operator!=(const name &o){return not (*this==o);}
Then if I have a class, all I need to declare is operator< and operator==:
class ttt{
//...
bool operator<(const ttt &o);
bool operator==(const ttt &o);
CREATE_COMPARITORS(ttt);
//...
};

What are C++ non-template members as used in the Barton-Nackman trick?

From wikipedia:
// A class template to express an equality comparison interface.
template<typename T> class equal_comparable
{
friend bool operator==(T const &a, T const &b) { return a.equal_to(b); }
friend bool operator!=(T const &a, T const &b) { return !a.equal_to(b); }
};
class value_type
// Class value_type wants to have == and !=, so it derives from
// equal_comparable with itself as argument (which is the CRTP).
: private equal_comparable<value_type>
{
public:
bool equal_to(value_type const& rhs) const; // to be defined
};
This is supposed to be the Barton-Nackman, that could achieve compile-time dimensional analysis (checking if some operations applied to variables end up in comparable numbers, like speed comparable to space/time but no acceleration).
Could anyone explain me how, or at least explain me what are the NON-TEMPLATE members?
Thanks
The rules of the language have changed since the pattern was invented, although care was taken not to break it. In other words, as far as I can tell, it still works but for different reasons than it originally did. I don't think I would base an attempt at dimensional analysis on this pattern as I think there are better ways of doing that today.
I also think the example is too trivial to be helpful. As already stated the instantiation of equal_comparable<value_type> causes operator== and operator!= for value_type to appear. Since they are non-members it doesn't matter that the inheritance is private, they're still eligable for selection when resolving a call. It's just hard to see the point in this example. Let's say however, that you add a template parameter to equal_comparable and a few other things:
template<typename U, typename V> class equal_comparable
{
friend bool operator==(U const &a, V const &b) { return a.equal_to(b); }
friend bool operator!=(U const &a, V const &b) { return !a.equal_to(b); }
};
class some_other_type
{
bool equal_to(value_type const& rhs) const;
};
class value_type
: private equal_comparable<value_type>, // value_type comparable to itself
private equal_comparable<some_other_type> // value_type comparable to some_other_type
{
public:
bool equal_to(value_type const& rhs) const;
bool equal_to(some_other_type const& rhs) const;
};
Disclaimer: I have no idea if this is the way it's supposed to be used but I'm reasonably sure that it would work as described.
These are actually nontemplate nonmembers - the comparison operators in the base template - they get used by the ADL for the derived class. A template member would be something like:
class C
{
...
template < typename T > void DoGreatStuff( T t ) { ... }
...
};
The instantiation of equal_comparable<value_type> in value_type class causes the compiler to generate two comparison functions:
friend bool operator==(value_type const &a, value_type const &b) { return a.equal_to(b); }
friend bool operator!=(value_type const &a, value_type const &b) { return !a.equal_to(b); }
These functions are nontemplate since they do not depend on any template parameter, but they are also nonmembers since they are declared as friend.