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.
Related
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 :)
I've got the following situation in a class template case:
template<class T1,class T2>
class targetClass{
public:
typedef typename std::pair<T1, T2> ToSortType;
typedef typename std::set<ToSortType> ContainerSort;
void bar(ToSortType a, ToSortType b);
private:
ContainerSort container;
bool operator<(const ToSortType& rhs) const;
}
template<class T1,class T2>
void targetClass<T1,T2>::bar(ToSortType a, ToSortType b){
container.insert(a);
container.insert(b);
}
template <class T1,class T2>
bool targetClass<T1,T2>::operator<(const ToSortType& rhs) const
{
return this->first < rhs.first;
}
In main function something like this:
targetClass<int,T2> anObjectTarget;
T2 a;
T2 b;
anObjectTarget.bar(std::make_pair(0,a),std::make_pair(1,b));
Where T2 is a user-defined type which generally does not have a defined operator<
In this particular case, std::set has to compare std::pair<int,T2> by firstly check an operator< (and others, maybe) for int type and then for T2. In this case, the compiler can not find a suitable operator for T2. In the previous snippet then I make a redefinition of the operator concerned but the compiler complains in this way:
/usr/include/c++/7/bits/stl_pair.h:456: error: no match for ‘operator<’ (operand types are ‘const T2’ and ‘const T2’)
|| (!(__y.first < __x.first) && __x.second < __y.second); }
~~~~~~~~~~~^~~~~~~~~~~~
I've never redefined an operator before but looking to the documentation it looks correct to me (but not to the compiler).
The operator you overloaded is a member of targetClass<T1,T2> and takes two ToSortType as parameter. Thats not how an overload of the < operator works. Consider that for instances of class type the follwing two are equivalent:
a < b
a.operator<(b)
ie operators are just syntactic sugar for calling special member functions. The operator you wrote could only be called like
targetClass<T1,T2> t;
T1 a;
T2 b;
t.operator<(a,b);
but what the set tries to call is a < b, ie a.operator(b) and that apparently does not exist (std::pair<T1,T2> can only be comared via < when both T1 and T2 can).
Long story short: You cannot use your operator to compare two instances of ToSortType.
I would not recommend to try to overload the operator< for std::pair<T1,T2>, but rather use a custom type:
template<class T1,class T2>
class targetClass{
public:
struct value_type {
T1 first;
T2 second;
bool operator<(const value_type& other) {
return first < rhs.first;
}
}
using container_type = std::set<value_type>;
void bar(const value_type& a,const value_type& b);
private:
container_type container;
};
If you want to stay with std::pair then you can use the fact that std::set allows you to chose the type of the comparator. However, first I have to explain a bit not to confuse you because the following may appear to be contradicting the above (it does not). The default comparator that set uses is std::less<Key>, that is a type with an operator() that compares two element of type Key, it is something similar (but not exactly) like this:
template <typename Key>
struct less {
bool operator() (const Key& a,const Key& b) const {
return a < b;
}
};
And this is the place where the compiler cannot find a < for your Key type (which is std::pair<T1,T2>). You can use your own comparator:
template <typename T1,typename T2>
struct my_comparator {
bool operator() (const std::pair<T1,T2>& a, const std::pair<T1,T2>& b) const {
return a.first < b.first;
}
};
And then your set is
using container_type = std::set<typename std::pair<T1,T2>,typename my_comparator<T1,T2>>;
I have defined operator== as follows:
template <class State>
bool operator==(const std::shared_ptr<const State> &lhs,
const std::shared_ptr<const State> &rhs) {
return *lhs == *rhs;
}
This operator does not get instantiated (in gdb, I cannot set the break-point on the return statement -- the line does not exist).
However, this operator should be used by std::find called in this line:
return std::find(v.begin(), v.end(), el) != v.end();
I checked the type of v in the above line in gdb:
(gdb) whatis v
type = const std::vector<std::shared_ptr<Domains::IncWorst const>> &
(gdb) whatis el
type = const std::shared_ptr<Domains::IncWorst const> &
Doesn't this match my templated operator== with State being IncWorst?
I implemented a toy example as follows and the example works, so I cannot understand why the real code does not.
template<class V, typename T>
bool in(const V &v, const T &el) {
return std::find(v.begin(), v.end(), el) != v.end();
}
struct MyState {
MyState(int xx) : x(xx) {}
bool operator==(const MyState &rhs) const {
return x == rhs.x;
}
int x;
};
template <class State>
bool operator==(const std::shared_ptr<const State> &lhs,
const std::shared_ptr<const State> &rhs) {
return *lhs == *rhs;
}
int main() {
std::vector<std::shared_ptr<const MyState>> v{
std::make_shared<const MyState>(5)};
auto p = std::make_shared<const MyState>(5);
std::cout << in(v, p) << std::endl; // outputs 1
return 0;
}
Your operator== template is in the wrong namespace.
In order to be found by ADL, it must be either in the std namespace (which would be illegal, per [namespace.std]/1) or in Domains (per [basic.lookup.argdep]/2).
However, this is still highly dangerous, since if any template performing an equality comparison (e.g. but not limited to std::find) is instantiated both before and after your operator== template is declared, your whole program will be invalid per [temp.point]/8 and [basic.def.odr]/6.
If you must provide operator overload templates for std::shared_ptrs of your types, prefer to explicitly instantiate them after the declaration of each class, such that there is less chance of a template being instantiated somewhere the class is not visible:
struct MyState {
// ...
};
template bool operator==<MyState>(
const std::shared_ptr<MyState const>&,
const std::shared_ptr<MyState const>&);
This could still be problematic if someone forward-declares MyState somewhere else, but it's probably the best you can do.
Given the following function:
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
return false;
return true;
}
how can I determine if the given T overrides the != operator? If someone uses a type without overloading it might end up using a simple binary comparison which might silently lead to wrong results. So I want to ensure only classes that have their own != operator overloading can be used here.
[update 1 - boost 'semi' solution]
I realized that example from my first answer does not work if your class has conversion operator (to type which allows for != comparision), to fix it you can use boost has_not_equal_to type trait. Still its not perfect, as in some cases it generates compilation error instead of giving a value. Those cases are listed in provided link.
[update 2 - concepts solution]
Example with use of concepts:
#include <iostream>
#include <vector>
template<typename T>
concept bool OperatorNotEqual_comparable()
{
return requires (T a, T b) {
{ a.operator!=(b) } -> bool;
};
}
template <OperatorNotEqual_comparable T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i]) // Requires that the != operator is supported by the template type.
return false;
return true;
}
struct sample1{
bool operator!=(const sample1&) const { return true; }
};
struct sample2{
};
struct sample3{
operator void*() { return 0; }
};
int main() {
// Compiles ok!
std::vector<sample1> vec1;
equals(vec1, vec1);
// Fails, which is OK!
//std::vector<sample2> vec2;
//equals(vec2, vec2);
// Fails, which is OK!
//std::vector<sample2*> vec2;
//equals(vec2, vec2);
// Fails, which is OK!
//std::vector<int> vec4;
//equals(vec4, vec4);
// Fails, which is OK!
//std::vector<sample3> vec5;
//equals(vec5, vec5);
}
http://melpon.org/wandbox/permlink/txliKPeMcStc6FhK
[old answer - SFINAE solution, does not check for conversion operator]
You can use SFINAE, and in the near future concepts (they are in gcc 6.0),
#include<iostream>
#include<string>
#include<type_traits>
template<typename T>
class has_not_equal{
template<typename U>
struct null_value{
static U& value;
};
template<typename U>
static std::true_type test(U*,decltype(null_value<U>::value!=null_value<U>::value)* = 0);
static std::false_type test(void*);
public:
typedef decltype(test(static_cast<T*>(0))) type;
static const bool value = type::value;
};
struct sample1{
bool operator!=(const sample1&) { return true; }
};
struct sample2{
};
int main(){
std::cout<<std::boolalpha;
std::cout<<has_not_equal<int>::value<<std::endl;
std::cout<<has_not_equal<std::string>::value<<std::endl;
std::cout<<has_not_equal<sample1>::value<<std::endl;
std::cout<<has_not_equal<sample2>::value<<std::endl;
}
output:
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
true
true
true
false
live
above code is a modified version from this site, it was for operator==, I changed it to operator!=
If T has no operator !=, then the template wont get instantiated for that type, but you will get a (possibly horribly long and unreadable) error message from your compiler. On the other hand, it T has a operator !=, then it should be just fine to use it. There wont be silent wrong result, unless Ts operator != is anyhow broken.
The only other (besides the conversion) case (I can think of) where a binary comparison can happen (has operator!= defined) and lead to silently wrong results is when T is actually a pointer and you expect a "deep comparison".
One could add an overload for vectors containg pointers but that wouldn't cover pointer to array storage.
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b)
{
return std::equal(a.begin(), a.end(), b.begin());
}
template <typename T>
static bool equals(const std::vector<T *> &a, const std::vector<T *> &b)
{
return std::equal(a.begin(), a.end(), b.begin()
[](T* ap, T* bp) -> bool { return *ap == *bp; });
}
If you are interested in check whether there is an operator != defined in a class (and it has precisely given signature) you may want this approach (tests taken from marcinj's code):
#include <iostream>
#include <vector>
#include <type_traits>
template <class T, class = void>
struct has_not_equal: std::false_type { };
template <class T>
struct has_not_equal<T, typename std::enable_if<std::is_same<decltype(static_cast<bool (T::*)(const T&)const>(&T::operator!=)), bool (T::*)(const T&)const>::value>::type >: std::true_type { };
struct sample1{
bool operator!=(const sample1&) const { return true; }
};
struct sample2{
};
struct sample3:sample2 {
bool operator!=(const sample2& b) const { return true; }
};
struct sample4:sample2 {
bool operator!=(const sample2& b) const { return true; }
bool operator!=(const sample4& b) const { return true; }
};
int main(){
std::cout<<std::boolalpha;
std::cout<<has_not_equal<int>::value<<std::endl;
std::cout<<has_not_equal<std::string>::value<<std::endl;
std::cout<<has_not_equal<sample1>::value<<std::endl;
std::cout<<has_not_equal<sample2>::value<<std::endl;
std::cout<<has_not_equal<sample3>::value<<std::endl;
std::cout<<has_not_equal<sample4>::value<<std::endl;
}
Output of the program:
false
false
true
false
false
true
You may easily add allowed operator != overloads by adding specialization of the has_not_equal struct...
Even though the title of my question here says I'm looking for a way to determine if the != operator is defined, the real question (as you can read from the description and my comment) was how to ensure that I don't silently get wrong results for a type T which has no != operator defined. The existing answers brought up good points, but the real answer is this:
1) You will get a compiler error on the if (a[i] != b[i]) line if you instantiate the template with a value type array using a type that's missing the != operator override (e.g. std::vector<sample2> from marcinj's answer) or you get hard to understand compiler error if you use std::equal instead. In this case the explicit comparison is much more helpful when looking for a solution.
2) If you have a reference type in the vectors to compare you will at first get no problem at all (since there are comparison operators defined for references, even though they only do a flat comparison via the address values). If you want to ensure that the comparison works as if you had used value types (including deep comparison) then add a specialization for vectors with pointer types, as pointed out by Pixelchemist. However, I cannot get the std::equals variants to compile at the moment.
At the end the solution that works for me is this:
template <typename T>
static bool equals(const std::vector<T> &a, const std::vector<T> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (a[i] != b[i])
return false;
return true;
}
template <typename T>
static bool equals(const std::vector<T*> &a, const std::vector<T*> &b) {
if (a.size() != b.size())
return false;
for (size_t i = 0; i < a.size(); ++i)
if (*a[i] != *b[i])
return false;
return true;
}
Tested with the sample1/2/3/4 structs from marcinj's answer. It's important to note that the operator overloading must use a value type (operator == (const sample1&)) instead of a reference type.
I have still upvoted all answers that gave me useful information to get the final answer.
If someone uses a type without overloading it might end up using a simple binary comparison which might silently lead to wrong results.
There's not such thing as "binary comparison" in C++. Either your class/struct has operator!= or the template won't be instantiated (and if there are no other candidates then it will be an error).
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.