C++ Template no match for operator< - c++

I've seen questions similar to this, but haven't found a solution that worked on my problem, so I was hoping I could get some help.
I want to pass a class as an argument to a function with template parameters (For this example I just want it to compile, so nvm that it doesn't make sense with return true in operators). I figured this way in the following code would work:
template<typename T>
class MyClass {
public:
bool operator>(const T&) const {
return true;
}
bool operator<(const T&) const {
return true;
}
};
template<typename T, typename S>
vector<T> between_interval(T* start, T* end, const S& low, const S& high){
vector<T> vec;
for ( ; start != end; start++) {
if (low < *start && *start < high)
vec.push_back(* start);
}
return vec;
}
int main() {
double v1[] = {1.23, 4.56, 7.89, -10, 4};
MyClass<double> k1;
MyClass<double> k2;
vector<double> res = between_interval(v1, v1 + 3, k1, k2);
for (auto x : res)
cout << x << " ";
cout << endl;
}
I get the following error(s):
u4.cpp: In instantiation of ‘std::vector<T> between_interval(T*, T*, const S&, const S&) [with T = double; S = MyClass<double>]’:
u4.cpp:39:55: required from here
u4.cpp:28:36: error: no match for ‘operator<’ (operand types are ‘double’ and ‘const MyClass<double>’)
if (low < *start && *start < high)
I realize passing K1 and K2 won't make sense as it is at the moment, but the compiler doesn't mention this, it complains about not having a match for operator<, so my main question is why? Is it too general with a template argument in operator<? Is there any other way to make the operators work but without using the templates?
Thank you

With
template<typename T>
class MyClass {
public:
bool operator>(const T&) const {
return true;
}
bool operator<(const T&) const {
return true;
}
};
and
MyClass <double> k1;
double d = 4.2;
you might do
k1 < d
k1 > d
but you do
d < k1 (with *start < high)
you have to implement that operator too or change the code to use the provided operator.

Because your operator < is a member function it expects the left hand operand to be of the class type. That is fine for low < *start but in *start < high you have a double as the left hand operand.
You will either need to provide a free function that takes a double as the first parameter or make your class convertible to a double (or T).

Member operators always use the left operand as this and the right operand as the operator argument. That means your comparison operators will only work when the MyClass is on the left of the comparison. The quick fix is to change the line if (low < *start && *start < high) to if (low < *start && high > *start) to put the MyClass instance on the left side of each comparison. The cleaner solution is to provide free operators which take a T on the left side and a MyClass<T> on the right side. For example :
template<typename T>
bool operator>(const T& p_left, const MyClass<T> & p_right) {
return p_right < p_left;
}
template<typename T>
bool operator<(const T& p_left, const MyClass<T> & p_right) {
return p_right > p_left;
}

Related

Why std::tuple comparison calls operator bool when operator< overload is provided

#include <tuple>
struct X {
int i = 0;
friend constexpr bool operator<(const X &l, const X &r) noexcept {
return l.i < r.i;
}
};
struct Y {
int i = 0;
constexpr operator bool() const noexcept {
return i != 0;
}
friend constexpr bool operator<(const Y &l, const Y &r) noexcept {
return l.i < r.i;
}
};
int main() {
constexpr X a{1}, b{2};
static_assert(std::tie(a) < std::tie(b));
constexpr Y c{1}, d{2};
static_assert(c < d);
// assert failed
// static_assert(std::tie(c) < std::tie(d));
return 0;
}
I'm compiling with C++20.
Line static_assert(std::tie(c) < std::tie(d)); will fail. It turns out that when comparing c and d, operator bool is called instead of operator<. Why would operator bool get involved in the first place?
I find this quite surprising. Is this intended or a bug?
When comparing two tuple<T>s, named t1 and t2, the operator<=> overload for tuple will use get<0>(t1) <=> get<0>(t2) to compare elements when type T satisfies three_­way_­comparable; otherwise, it will use operator<.
Since Y is implicitly convertible to bool, this makes y <=> y a valid expression and satisfies three_­way_­comparable, such that std::tie(c) < std::tie(d) (unexpectedly) invokes bool(c) <=> bool(d) in your example.

Does the constructor not support the specialization of template functions?

I tried to let my class's constructor function became a specialization template function, but I failed the compilation. Does the constructor not support the specialization of template functions? If do, how can I add specialization template function to my class's constructor function?
#include <iostream>
#include <vector>
using namespace std;
class Bignum {
private:
vector<char> vec;
public:
template <class Type> Bignum(const vector<Type> &num) {
vec = num;
}
template <class Type> Bignum(Type &num) {
while (num) {
vec.push_back(num % 10);
num /= 10;
}
}
template <> Bignum(const string &str) {
auto __first = str.rbegin();
auto __last = str.rend();
for (auto i = __first; i <= __last; ++i) {
vec.push_back(*i);
}
}
template <class Type> Bignum(const Type *__first, const Type *__last) {
for (const Type *i = __last; i >= __first; --i) {
vec.push_back(*i);
}
}
template<> Bignum(char *__first, char *__last) {
for (char * i = __last; i >= __first; --i) {
vec.push_back(*i - '0');
}
}
Bignum () {
}
friend ostream& operator << (ostream &os, const Bignum &num) {
auto __first = num.vec.rbegin();
auto __last = num.vec.rend();
for (auto i = __first; i < __last; ++i) {
os << (char)(*i + '0') << ' ';
}
os << (char)(*__last + '0') << endl;
return os;
}
size_t size() const {
return vec.size();
}
const char & at(const size_t &pos) const {
return vec.at(vec.size() - pos);
}
const char & operator [] (const size_t &pos) {
return vec.at(pos);
}
};
int main(int argc, const char * argv[]) {
return 0;
}
I received a compilation error at line 29 which says No function template matches function template specialization 'Bignum'.
As mentioned in the comments, you just need to remove the template <> from the constructors that do not need to be member function templates.
However, it may be beneficial to consider how to accomplish what you may be after in a more generic fashion.
How you actually intend to store the data is confusing, because your implementation does different things (some expect it to be stored in "little-endian" and others expect it in "big-endian" order). In addition, some of your functions expect the data to be stored in the range [0, 9] and others seem to expect data in the range ['0', '9'].
However, none of that is relevant to what I think is the real question, which is about the constructor interface, so I will assume that data is stored as ASCII digits ['0', '9'] and in the order they would be printed as a string - just to pick something.
I would also suggest you use a specific strong type Digit, as the internal representation, which would remove all ambiguity as to the meaning. It would also remove all change of undesired conversions.
It looks like you want to be able to create a BigNum out of anything that can be used to create a vector of char.
This constructor does that. Note that we explicitly disable taking a single integral type, because you can create a vector from one of those, but it does not seem to be what you want, since you have another constructor that appears to take an integral.
template <
typename... ArgTs,
std::enable_if_t<
std::is_constructible_v<std::vector<char>, ArgTs...> &&
not ((sizeof...(ArgTs) == 1) && ... && std::is_integral_v<ArgTs>),
bool> = true>
explicit Bignum(ArgTs && ... args)
: vec(std::forward<ArgTs>(args)...)
{
// If not using a strong type, should at least assert that the
// data you get is what you expect.
assert(vec.size() == std::count_if(
vec.begin(),
vec.end(),
[](unsigned char c){ return std::isdigit(c); }));
}
Then, you can take any unsigned integral type like this (I restricted it to unsigned because it does not look like your code is prepared to handle signed values).
template <
typename T,
std::enable_if_t<
std::is_integral_v<T> && std::is_unsigned_v<T>,
bool> = true>
Bignum(T number)
{
while (number) {
vec.push_back((number % 10) + '0');
number /= 10;
}
std::reverse(vec.begin(), vec.end());
}
And, to accept a string, you do not need a member function template...
Bignum(std::string_view s)
: Bignum(s.begin(), s.end())
{ }
Finally, I'll encourage you to become familiar with the standard algorithms. For example,
friend std::ostream & operator << (std::ostream & os, Bignum const & num)
{
std::copy(num.vec.begin(), num.vec.end(), std::ostream_iterator<char>(os));
return os;
}

How to overload [] for a templated function

I'm tasked with improving C arrays by implementing an array class with various functionality. I'm trying to overload '[]' but I've ran into an issue.
template<typename T>
class SA{
private:
T* pT;
//other vars...
public:
//other initialization functions...
int& operator [](int offset);
};
template<typename T>
int& SA<T>::operator [](int offset){
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}
int main(){
SA<float> thing1(15);
thing1[5] = 5;
return 0;
When I use this overloading function, I'm met with this error:
error: cannot bind non-const lvalue reference of type ‘int&’ to an
rvalue of type ‘int’
However, if I understand correctly I can't re-assign the value at this index with '=' if I remove the '&'. The compiler will also throw this error if I do so:
error: lvalue required as left operand of assignment
What am I supposed to do here?
The return type of the function should be T&, not int&:
T& operator [](int offset);
Also note that, while it is nice that you check to make sure the index is in range, you don't actually do anything about it. One solution is to throw an exception:
#include <stdexcept>
template <typename T>
T& SA<T>::operator[](int offset) {
if (offset < lIdx || offset > hIdx)
throw std::out_of_range{"Array index out of range"};
return pT[offset];
}
If you really want to be consistent with STL containers, let operator[] be unchecked, and add a debug-time check like:
#include <cassert>
template <typename T>
T& SA<T>::operator[](int offset) noexcept { // note noexcept
assert(offset < lIdx || offset > hIdx);
return pT[offset];
}
Adding a const-overload of this operator would also be nice:
const T& operator [](int offset) const noexcept;
first you should adjust the return type according to your value type:
template<typename T>
T& SA<T>::operator [](int offset){
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}
Then if you want it to also work in constant mode add the following overload:
template<typename T>
const T& SA<T>::operator [](int offset)const{
if (offset < lIdx || offset > hIdx){
std::cout << "invalid index:" << std::endl;
}
return pT[offset];
}

Could I use operator == if I only implemented operator <?

I have implemented operator< for a certain object.
Logically, if !(a < b) and !(b < a) it means a == b.
Is this inferred automatically? Can I use == if I only implement <?
C++ cannot infer this automatically for a couple of reasons:
It doesn't make sense for every single type to be compared with operator<, so the type may not necessarily define a operator<.
This means that operator== cannot be automatically defined in terms of operator<
operator< isn't required to compare its arguments. A programmer can define operators for their types to do almost anything to their arguments.
This means that your statement about !(a < b) && !(b < a) being equivalent to a == b may not necessarily be true, assuming those operators are defined.
If you want an operator== function for your types, just define one yourself. It's not that hard :)
// For comparing, something like this is used
bool operator==(const MyType& lhs, const MyType& rhs)
{
// compare (or do other things!) however you want
}
// ... though it's not the only thing you can do
// - The return type can be customised
// - ... as can both of the arguments
const MyType& operator==(int* lhs, const MyType* const rhs)
{
return lhs;
}
It cannot infer == from < because not all types are ordered, like std::complex. Is 2 + 3i > 1 + 4i or not?
Moreover even in types that are normally ordered you still can't infer the equality from > or <, for example IEEE-754 NaN
double n = std::numeric_limits<double>::quiet_NaN();
std::cout << "NaN == NaN: " << (n == n) << '\n';
std::cout << "NaN < NaN: " << (n < n) << '\n';
std::cout << "NaN > NaN: " << (n > n) << '\n';
std::cout << "NaN != NaN: " << (n != n) << '\n';
They'll all return false except the last one
No. This method works well on number-like objects that is called totally ordered. For all kinds of set/class, no one can guarantee this relation. Even no one can guarantee a operator < would compare something.
So == is nothing else than ==. You may implement == by < but this doesn't work for everyone and C++ standards won't do it for you.
Logically, if !(a < b) and !(b < a) it means a == b. Does c++ infer
this automatically? Can I use == if I only implemented
To put what others have stated in mathematical terms: Assuming that you have an operator < that returns bool and defines a strict weak order, and you implement operator == as returning !(a < b) && !(b < a), then this operator defines an equivalence relation consistent with the given strict weak order. However, C++ neither requires operator < to define a strict weak order, nor operator == to define an equivalence relation (although many standard algorithms such as sort may implicitly use these operators and require a strict weak order rsp. equivalence relation).
If you want to define all the other relational operators based on and consistent with your operator <'s strict weak order, Boost.Operators may save you some typing.
Because it's so easy to misuse an operator < that does not meet the standard algorithm's requirements, e.g. by accidentally using it via std::sort, std::lower_bound etc., I recommend to define operator < either as a strict weak order or not at all. The example CodesInChaos gave is a partial order, which does not meet the "transitivity of incomparability" requirement of a strict weak order. Therefore, I'd recommend calling such a relation by a different name, e.g. bool setLess(const MySet &, const MySet &).
Sources:
https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings
http://en.cppreference.com/w/cpp/concept/Compare
C++ does not infer this automatically. For operator>, operator<= and operator>=, you could use std::rel_ops; this requires only operator<. However, it does not provide operator== in terms of operator<. You can do this yourself like this:
template <class T>
bool operator==(T const& lhs, T const& rhs)
{
return !((lhs < rhs) or (rhs < lhs));
}
Note that: !((lhs < rhs) or (rhs < lhs)) and !(lhs < rhs) and !(rhs < lhs) are equivalent, mathematically.
The compiler doesn't infer == from <.
You can check that with a simple example:
#include <iostream>
struct A {
A(int r):i{r}{}
int i;
};
bool operator<(A const & a1, A const& a2) {
return a1.i < a2.i;
}
int main(int argc, char* argv[]) {
A a1{2};
A a2{3};
if(a1 == a2) {
std::cout << "equals\n";
}
return 0;
}
GCC gives you this error:
main.cpp:20:11: error: no match for 'operator==' (operand types are 'A' and 'A')
if(a1 == a2) {
As many have stated, no you cannot, and no the compiler should not.
This doesn't mean it shouldn't be easy to go from a < to == and the whole myriad.
boost::operators attempts to make it easy. Use it and done.
If you want to do it yourself, it also only takes a little bit of code to reimplement what boost provides you:
namespace utility {
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = ::utility::details::can_apply<Z,void,Ts...>;
}
namespace auto_operators {
template<class T, class U>
using less_r = decltype( std::declval<T const&>() < std::declval<U const&>() );
template<class T, class U>
using can_less = ::utility::can_apply<less_r, T, U>;
struct order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator>(T const& lhs, U const& rhs) {
return rhs < lhs;
}
template<class T, class U>
friend enabled<U,T>
operator<=(T const& lhs, U const& rhs) {
return !(lhs > rhs);
}
template<class T, class U>
friend enabled<T,U>
operator>=(T const& lhs, U const& rhs) {
return !(lhs < rhs);
}
};
struct equal_from_less:order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{} && can_less<U,T>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator==(T const& lhs, U const& rhs) {
return !(lhs < rhs) && !(rhs < lhs);
}
template<class T, class U>
friend enabled<U,T>
operator!=(T const& lhs, U const& rhs) {
return !(lhs==rhs);
}
};
}
The above only has to be written once, or equivalent cose gotten from #include boost.
Once you have boost, or the above, it is as simple as something like:
struct foo : auto_operators::equal_from_less {
int x;
foo( int in ):x(in) {}
friend bool operator<( foo const& lhs, foo const& rhs ) {
return lhs.x < rhs.x;
}
};
and foo now has all the ordering and comparison operators defined on it.
int main() {
foo one{1}, two{2};
std::cout << (one < two) << "\n";
std::cout << (one > two) << "\n";
std::cout << (one == two) << "\n";
std::cout << (one != two) << "\n";
std::cout << (one <= two) << "\n";
std::cout << (one >= two) << "\n";
std::cout << (one == one) << "\n";
std::cout << (one != one) << "\n";
std::cout << (one <= one) << "\n";
std::cout << (one >= one) << "\n";
}
Live example.
The point of all of this is that C++ doesn't, as a language, assume that < means > and >= and == all make sense. But you can write a library that lets you take a type with < defined, and adding a trivial base class suddenly make all of those other operations defined with zero runtime cost.
There are templates defined in the std::rel_ops namespace which are auto-defining missing operators.
It doesn't define an equality operator based on the less operator as you wish.
Still this is quite useful; if you define the less operator and equality operator you will have the other comparison operators for free.
The answer is NO, you just need a simple test
struct MyType{
int value;
};
bool operator < (MyType& a, MyType& b)
{
return a.value < b.value;
}
int main(int argc, char* argv[])
{
MyType a = {3};
MyType b = {4};
if (a == b)
std::cout << "a==b" << std::endl;
if (a < b)
std::cout << "a < b" << std::endl;
}
g++ 4.8.2 complains:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:16:11: error: no match for ‘operator==’ (operand types are ‘MyType’ and ‘MyType’)
But there is something similar that works in C++, check this c++ concepts:Compare
it says:
equiv(a, b), an expression equivalent to !comp(a, b) && !comp(b, a)
In addition to other answers,
The compiler cannot even infer != from ==
struct MyType
{
int value;
};
bool operator == (const MyType& a, const MyType& b)
{
return a.value == b.value;
}
int main()
{
MyType a = {3};
MyType b = {4};
if (a != b) // (* compilation Error *)
std::cout << "a does not equal b" << std::endl;
}
It would be nice though if there is an option to tell the compiler that the rest of the rational operators apply to your class.
There is, as explained in some answers in the <utility> header something that can provide such functionality. you will need to add the following line at the beginning of the main:
using namespace std::rel_ops;
However, using this approach is costly and will cause overload ambiguities all over the place as noted by JDługosz.
Consider the following example:
class point{
unsigned int x;
unsigned int y;
public:
bool operator <(const point& other){
return (x+y) < (other.x+other.y);
}
bool operator == (const point& other){
return (x==other.x) && (y==other.y);
}
}
And then we have:
point a{1, 2};
point b{2, 1};
!(a < b), !(b < a) , but also !(a == b).
Kind of.
But you would need boost::operators
Overloaded operators for class types typically occur in groups. If you
can write x + y, you probably also want to be able to write x += y. If
you can write x < y, you also want x > y, x >= y, and x <= y.
Moreover, unless your class has really surprising behavior, some of
these related operators can be defined in terms of others (e.g. x >= y
<=> !(x < y)). Replicating this boilerplate for multiple classes is
both tedious and error-prone. The boost/operators.hpp templates help
by generating operators for you at namespace scope based on other
operators you've defined in your class.
The answer is clear NO. There is no implicit way. C++ classes allow operators to be overloaded. So, your idea that logically, if !(a < b) and !(b < a) it means a == b. is correct. And, you can overload operators as below. For example, a Fraction class:
class Fraction {
int num;
int denom;
. . .
public:
. . .
bool operator < (const Fraction &other) {
if ((this->num * other.denom) < (this->denom * other.num))
return false;
else
return true;
}
bool operator == (const Fraction &other) (
if (!(*this < other) && !(other < *this)) {
return true;
else
return false;
}
};

How to determine if a class implements != operator overloading?

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