I am making a project that takes in types as templates. The operator== is already overloaded for chars, ints, strings, etc as you know, but if the user decides to pass in a cstring (null terminated character array) I will need to overload the == for that. Can I choose to only overload the operator== when the user uses cstrings, and use the default == when they dont? How would this be accomplished?
You cannot overload the == operator on a C-string. I am not completely sure why that should be necessary - the C++ string class has defined an implicit conversion from a C-string, and already defines the == operator.
You can't overload operator== for C strings, because they are pointers and operators can be overloaded if at least one operand is a class or enum. What you can do is create your own comparator function and use it in your code instead of ==:
template<typename T>
bool my_equal(const T& a, const T& b) {
return a == b;
}
bool my_equal(const char* a, const char* b) {
return /* your comparison implementation */;
}
Update: you may have to add more overloads to support std::string vs const char* comparisons, as pointed out by TonyD in comments.
You can use type traits to dispatch to the correct function. For example:
#include <type_traits>
template<typename T>
using is_cstring =
std::integral_constant<bool,
std::is_same<T, char const*>::value
|| std::is_same<T, char*>::value>;
template<typename T>
class Thingy
{
public:
bool operator==(Thingy const& rhs) const
{
return equal_helper(rhs, is_cstring<T>());
}
private:
bool equal_helper(Thingy const& rhs, std::true_type) const
{
return strcmp(m_value, rhs.m_value) == 0;
}
bool equal_helper(Thingy const& rhs, std::false_type) const
{
return m_value == rhs.m_value;
}
T m_value;
};
Related
I want to write a proxy class, that takes a template value and can be compared to any class that the template can be compared to.
template <class T>
class Proxy {
public:
Proxy(T value) : _value(value) {}
template <class U> // this should exist only if the T == U operator is defined
bool operator==(U const& other) const { return _value == other; }
template <class U> // this should exist only if U == T is defined
friend bool operator==(U const& first, Proxy<T> const& second) const { return first == second._value; }
private:
T _value;
};
for example, since this is legal code:
bool compare(std::string first, std::string_view second) {
return first == second;
}
I want this to be legal, too:
bool compare(std::string first, Proxy<std::string_view> second) {
return first == second;
}
but just to clarify, this should work for any classes that can be compared, or can be implicitly converted in order to be compared. Can I define a template conditional that will check for either case?
Since your criteria is essentially whether or not an expression like _value == other is well-formed, you can just rely on expression SFINAE to test it.
template <class U> // this should exist only if the T == U operator is defined
auto operator==(U const& other) const -> decltype(_value == other)
{ return _value == other; }
template <class U, std::enable_if_t<!std::is_same<U, Proxy>::value, int> = 0> // this should exist only if U == T is defined
friend auto operator==(U const& first, Proxy const& second) -> decltype(first == second._value)
{ return first == second._value; }
It may not be very DRY, since we need to repeat the expression twice, but it's a fairly simple way to do SFINAE, which is a major plus.
The other thing to note is that we do not want the second overload to be considered recursively, which may happen upon comparison of two proxies. So we need another SFINAE condition, spelled out the old fashioned way with enable_if, to discard the overload when U is a Proxy. This relies on the C++14 feature whereby substitutions are checked in declaration order. Putting it first prevents the recursion in first == second._value.
I'm writing a class named Double which extends the built in type 'double' in c++. It has a data member of the type 'double'. For the class Double I need to overload many basic arithmetic operators like "+", "-", "*", "/". For example, the "+" operator is overloaded this way:
Relation<Double>* operator+ (Double &d2)
// Relation is a abstract class template.
{
/*
...code that do something else.
*/
return new Plus<Double>(this, &d2); // Plus is derived from Relation.
}
// Double + Double returns a Relation pointer.
and the "-" operator is overloaded fast the same way:
Relation<Double>* operator- (Double &d2)
{
/*
...code that do something else but the same as above.
*/
return new Minus<Double>(this, &d2);
}
The actual calculation is done by the member function in the Relation class. The only difference of the operators' bodies is the object initialized(Plus vs Minus).
For operator that takes exactly two operands, I should alway do the overloading like this, which is duplicated and not nice.
So template function comes to my mind. But the problem is, I can pass Plus or Minus as template argument, but can not pass the operator. How could I make a template or use other methods to do the overloading of these operators?
Yes, operator overloading may be a pain and source of code duplication, see this suggestion to the standard to ease it.
For now the only I can think about is something like this:
template<typename T>
struct OperatorMinus {
static T doIt(T const& lhs, T const& rhs) { return lhs - rhs; };
}
template<typename T>
struct OperatorPlus {
static T doIt(T const& lhs, T const& rhs) { return lhs + rhs; };
}
template<typename T, typename U>
class Operator: public Relation<T>
public:
Operator(T const& lhs, T const& rhs): _lhs(lhs), _rhs(rhs) {}
T doIt() override {
return U::doIt(_lhs, _rhs);
}
private:
T _lhs;
T _rhs;
};
Relation<Double>* operator+ (Double &d2)
{
return new Operator<Double, OperatorPlus<Double>>(this, &d2);
}
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).
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);
}
I'm trying to remove a class object from list<boost::any> l
l.remove(class_type);
I tried writing something like this as a member function
bool operator == (const class_type &a) const //not sure about the arguments
{
//return bool value
}
How would you write an overload function to remove an object of class from a std::list of boost::any?
While your signature for operator== looks fine, overloading it for class_type isn't enough as boost::any doesn't magically use it. For removing elements however you can pass a predicate to remove_if, e.g.:
template<class T>
bool test_any(const boost::any& a, const T& to_test) {
const T* t = boost::any_cast<T>(&a);
return t && (*t == to_test);
}
std::list<boost::any> l = ...;
class_type to_test = ...;
l.remove_if(boost::bind(&test_any<class_type>, _1, to_test));