C++ Trick to avoid pointer comparison - c++

I am moving a code base from one programming style to another.
We have a type called Operand defined like:
class Operand
{...};
Then we had
class OperandFactory
{
public:
const Operand *make_operand (...);
};
OperandFactory used to hash the Operand and keep it in a table. Therefore if you called make_operand with the same arguments, you would get the same pointer and pointer comparison over Operands proliferated. Now I need to add a feature that will make this infeasible. So, I implement operator== in Operand and would like to somehow generate at compile time (better) or run time (better than nothing) error if I ever do a pointer comparison on Operands. What's the best way to achieve this?
This is only to be used during this transition stage, so I don't mind if the solution looks like a hack as long as it captures all comparisons in the code base.

you can overload the address of operator to return a handle and declare the comparison of two handles (without definition). This would lead to a linker error.
#include <iostream>
class Op;
class Handle {
Op *pri_;
public:
explicit Handle(Op *o) : pri_(o) {}
Op *operator->() const { return pri_; }
Op &operator*() const { return *pri_; }
};
// force compile time errors on comparison operators
bool operator==(const Handle &, const Handle &) = delete;
bool operator!=(const Handle &, const Handle &) = delete;
bool operator>=(const Handle &, const Handle &) = delete;
bool operator<=(const Handle &, const Handle &) = delete;
bool operator<(const Handle &, const Handle &) = delete;
bool operator>(const Handle &, const Handle &) = delete;
class Op {
int foo_;
public:
explicit Op(int i) : foo_(i) { }
Handle operator&() { return Handle(this); };
void touch() const { std::cout << "foobar"; }
};
int main(int argc, char **argv) {
Op i{10};
Op j{20};
auto c = &j; // works
c->touch(); // works
(*c).touch(); // works
if (&j == &i) {
/* will not compile */
}
}
Note:
You have to fulfill the random_access_iterator requirement for Handle!
Op i{10}
Handle ref = &i;
ref++; ref--; ++ref; --ref; ref = ref + 10; ref = ref - 10; // should all work.

Adding an operator in your Operand class won't help : you want to detect comparisons of pointers to Operands. Unfortunately, native types operators can't be overloaded, and pointers are of native type. This is not the solution you're looking for.

Related

How to overload polymorphic == and != operator in c++

class Media {
public:
bool operator==(const Media& other) const {}
bool operator!=(const Media& other) const {}
};
class Book : public Media {
public:
bool operator==(const Book& other) const {} // commenting out this line solves this issue.
bool operator!=(const Book& other) const {}
};
class Game : public Media {
public:
bool operator==(const Game& other) const {}
bool operator!=(const Game& other) const {}
};
int main() {
Book book;
Game game;
bool res = book == game; // doesn't compile.
}
I have these 3 classes and they must have their own == and != operators defined. But then I also have to compare between two siblings using those operators.
I could've written a (pure) virtual function, say, virtual bool equals(const Media& other) const in the base class that subclasses override. And then call that function in the bodies of == and != opertor definition in base class Media. But that feature is gone when I add another bool operator==(const Book& other) const {} in the Book class (the same goes for the Game class too).
Now I want to compare between siblings using those operators and still have all 6 definition in those 3 classes. How do I make it work?
You mentioned in the comments that this form of comparison is an imposed restriction (to compare among siblings of a child type). If its an imposed restriction that you need to somehow perform this with inheritance, then one option is to fulfill the base signature and use dynamic_cast. Note that this is not a clean approach, but it might be the expected solution for this problem if this is some form of assignment.
dynamic_cast uses Runtime Type Information (RTTI) to determine whether an instance to a base class is actually an instance of the derived class. When you use it with a pointer argument, it returns nullptr on failure -- which is easily testable:
auto p = dynamic_cast<const Book*>(&other);
if (p == nullptr) { // other is not a book
return false;
}
// compare books
You can use this along with a virtual function to satisfy the hierarchy. However, to avoid possible ambiguities with c++20's generated symmetric operator==/operator!= functions, it's usually better to do this through a named virtual function rather than the operator== itself in order to prevent ambiguity:
class Media {
public:
virtual ~Media() = default;
bool operator==(const Media& other) const { return do_equals(other); }
private:
virtual bool do_equals(const Media& other) const = 0;
};
class Book : public Media {
...
private:
bool do_equals(const Media& other) const override {
auto* p = dynamic_cast<const Book*>(&other);
if (p == nullptr) { return false; }
return (... some comparison logic ...);
}
...
};
... Same with Game ...
Since we never define operator==(const Book&) or operator==(const Game&), we won't see this shadow the base-class' operator==; instead it always dispatches through the base's operator==(const Media&) -- which is non-virtual and prevents ambiguity.
This would allow a Book and a Game to be comparable, but to return false -- whereas two Book or two Game objects may be compared with the appropriate logic.
Live Example
That said...
This approach is not a good design, as far as software architecture goes. It requires the derived class to query what the type is -- and usually by the time you need to do this, that's an indication that the logic is funky. And when it comes to equality operators, it also leads to complications with symmetry -- where a different derived class may choose to compare things weirdly with different types (imagine a Media that may compare true with other different media; at which point, the order matters for the function call).
A better approach in general is to define each of the respective equality operators between any types that logically require equality comparison. If you are in C++20 this is simple with symmetric equality generation; but pre-C++20 is a bit of a pain.
If a Book is meant to be comparable to a Game, then define operator==(const Game&) or operator==(const Book&, const Game&). Yes, this may mean you have a large number of operator==s to define for each of them; but its much more coherent, and can get better symmetry (especially with C++20's symmetric equality):
bool operator==(const Game&, const Book&);
bool operator==(const Book&, const Game&); // Generated in C++20
bool operator==(const Game&, const Game&);
bool operator==(const Book&, const Book&);
In an organization like this, Media may not even be logical as a 'Base class'. It may be more reasonable to consider some form of static polymorphism instead, such as using std::variant -- which is touched on in #Jarod42's answer. This would allow the types to be homogeneously stored and compared, but without requiring casting from the base to the derived type:
// no inheritance:
class Book { ... };
class Game { ... };
struct EqualityVisitor {
// Compare media of the same type
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; }
// Don't compare different media
template <typename T, typename U>
bool operator()(const T&, const U&) const { return false; }
};
class Media
{
public:
...
bool operator==(const Media& other) const {
return std::visit(EqualityVisitor{}, m_media, other.m_media);
}
private:
std::variant<Book, Game> m_media;
};
Live Example
This would be my recommended approach, provided the forms of media are meant to be fixed and not extended.
You might do double dispatch thanks to std::visit/std::variant (C++17):
class Media;
class Book;
class Game;
using MediaPtrVariant = std::variant<const Media*, const Book*, const Game*>;
class Media {
public:
virtual ~Media () = default;
virtual MediaPtrVariant asVariant() const { return this; }
};
class Book : public Media {
public:
MediaPtrVariant asVariant() const override { return this; }
};
class Game : public Media {
public:
MediaPtrVariant asVariant() const override { return this; }
};
struct EqualVisitor
{
template <typename T>
bool operator()(const T*, const T*) const { return true; }
template <typename T, typename U>
bool operator()(const T*, const U*) const { return false; }
};
bool operator ==(const Media& lhs, const Media& rhs)
{
return std::visit(EqualVisitor(), lhs.AsVariant(), rhs.AsVariant());
}
bool operator !=(const Media& lhs, const Media& rhs)
{
return !(lhs == rhs);
}
int main()
{
Book book;
Game game;
bool res = book == game;
}
Demo

Preventing implicit conversion operator only for binary operators

I'm having an issue that I've boiled down to the following, where an == operator usage compiles even though it's supposed to fail (C++17, tested on GCC 5.x, 8.x, and 9.x):
template <int N> struct thing {
operator const char * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
};
int main () {
thing<0> a;
thing<1> b;
a == b; // i don't want this to compile, but it does
}
The reason it is compiling is because the compiler is choosing to do this:
(const char *)a == (const char *)b;
Instead of this:
// bool thing<0>::operator == (const thing<0> &) const
a.operator == (b);
Because the latter isn't a match, since b is a thing<1> not a thing<0>. (Incidentally, it also produces unused-comparison warnings when the primitive comparison operator is used; not interesting, but that's why those warnings appear.)
I've verified this (actually it's how I discovered the cause) on C++ Insights, which outputs:
template <int N> struct thing {
operator const char * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
};
/* First instantiated from: insights.cpp:7 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct thing<0>
{
using retType_2_7 = const char *;
inline operator retType_2_7 () const
{
return nullptr;
}
inline bool operator==(const thing<0> &) const;
// inline constexpr thing() noexcept = default;
};
#endif
/* First instantiated from: insights.cpp:8 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct thing<1>
{
using retType_2_7 = const char *;
inline operator retType_2_7 () const
{
return nullptr;
}
inline bool operator==(const thing<1> &) const;
// inline constexpr thing() noexcept = default;
};
#endif
int main()
{
thing<0> a = thing<0>();
thing<1> b = thing<1>();
static_cast<const char *>(a.operator const char *()) == static_cast<const char *>(b.operator const char *());
}
Showing the conversion operator usage in that last line of main.
My question is: Of course, the whole thing behaves properly if I make the conversion operator explicit, but I'd really like to keep the implicit conversion operator and have the compiler enforce correct usage of operator == (where "correct" = failing to compile if the parameter type is different). Is this possible?
Note that it is not important to me to have thing<N> == const char * work. That is, I don't need this overload:
bool thing<N>::operator == (const char *) const
So I don't care if a solution breaks that flavor of ==.
I did search through other posts here; there were a number with misleadingly similar titles that ended up being unrelated. It looks like this post had a related problem (undesired implicit conversions) but it's still not applicable.
For completeness, here is a slightly less minimal but more representative example of what I'm actually doing, where the intent is to allow == to work for thing<T,N> and thing<R,N>, that is, the N's must be the same but the first template parameter can differ. I'm including this in case it affects a possible solution, since this is what I really need correct behavior for:
template <typename T, int N> struct thing {
operator const char * () const { return nullptr; }
template <typename R> bool operator == (const thing<R,N> &) const { return false; }
};
int main () {
thing<int,0> i0;
thing<float,0> f0;
thing<int,1> i1;
i0 == f0;
f0 == i0;
i0 == i1; // should fail to compile
f0 == i1; // should fail to compile
i1 == i0; // should fail to compile
i1 == f0; // should fail to compile
}
You can just provide a deleted version of the operator for the case where it shouldn't work, e.g.:
template <int N> struct thing {
operator const void * () const { return nullptr; }
bool operator == (const thing<N> &) const { return false; }
template <int X>
bool operator == (const thing<X> &) const = delete;
};
int main () {
thing<0> a;
thing<1> b;
a == a; // This compiles
a == b; // Doesn't compile
}
With C++20 the logic conveniently also extends to operator!=. With pre-C++20 compiler you should probably add corresponding overloads for operator!=, too.
If you mark the operator const char * as explicit, then your code will not compile.
explicit requires C++11, but you're already using nullptr, so that's not a problem.

Understanding C++ choice of conversion operator

I've got an optional-like class (I can't use optional since it's in C++17). It contains a (possible) value along with a flag indicating if it's valid. I've got an explicit bool operator and a conversion operator to get the value out. The problem is, sometimes C++ will choose the bool operator in an explicitly bool context (an if statement), and other times it won't. Can anyone help me understand this behavior:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() {
return std::move(value());
}
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1) {
double p = m1;
(void)p;
}
}
Whenever you write:
if (x)
That is equivalent to having written:
bool __flag(x);
if (__flag)
This is called a contextual conversion to bool (note that it's direct-initialization, so the explicit conversion function is a candidate).
When we do overload resolution on that construction for m0, there's only one valid candidate: explicit operator bool() const.
But when we do overload resolution on that construction for m1, there are two: explicit operator bool() const and operator double&&(), because double is convertible to bool. The latter is a better match because of the extra const qualification on the bool conversion function, even though we have to do an extra double conversion. Hence, it wins.
Would simply remove operator T&&() from your interface, as it doesn't make much sense for this type.
As soon as T is convertible to bool (double is, std::pair is not) your two operators will match and you'll get an ambiguous call, or one may be a better match for some reason.
You should only provide one of the two operators, not both.
Your operator bool and conversion operator are ambiguous in this design.
In the first context, std::pair<int,int> does not cast to bool
so the explicit bool conversion operator is used.
In the second context, double does cast to bool so the T
conversion operator is used, which returns a double, which then
casts to bool implicitly.
Note in the second context, you are calling std::move which puts value in a valid but undefined state, which leads to undefined behavior when you cast value to double a second time in the if block.
I'd use a named member function to indicate if it is valid, and modify the conversion operator:
#include <algorithm>
#include <stdexcept>
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
operator T&&() && { return std::move(value_); } // if rvalue
operator T&() & { return value_; } // if lvalue
operator const T&() const & { return value_; } // if const lvalue
bool valid() const { return valid_; }
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
int main() {
// fine, uses operator bool()
maybe<std::pair<int,int>> m0;
if (m0.valid()) {
std::pair<int,int> p = m0;
(void)p;
}
// throws error, uses operator T&&()
maybe<double> m1;
if (m1.valid()) {
double p = m1;
(void)p;
}
}
EDIT: The conversion operator should only move from member value_ if the maybe object is an rvalue reference. Using && after a functions signature specializes for this case -- please see Kerrek SB's answer for more information.

Unintended behavior from boost::operators

I'm taking boost::operators (clang 2.1, boost 1.48.0) for a spin, and ran into the following behavior I can't explain. It seems that when I add my own operator double() const method to my class Ex (as I'd like to allow my users to idiomatically use static_cast<double>() on instances of my class), I no longer get a compiler error when trying to use operator== between dissimilar classes. In fact, it seems that operator== is not called at all.
Without operator double() const, the class works completely as expected (save for that it now lacks a conversion operator), and I receive the correct complier error when trying f == h.
So what is the right way to add this conversion operator? Code below.
// clang++ -std=c++0x boost-operators-example.cpp -Wall -o ex
#include <boost/operators.hpp>
#include <iostream>
template <typename T, int N>
class Ex : boost::operators<Ex<T,N>> {
public:
Ex(T data) : data_(data) {};
Ex& operator=(const Ex& rhs) {
data_ = rhs.data_;
return *this;
};
T get() {
return data_ * N;
};
// the troubling operator double()
operator double() const {
return double(data_) / N;
};
bool operator<(const Ex& rhs) const {
return data_ < rhs.data_;
};
bool operator==(const Ex& rhs) const {
return data_ == rhs.data_;
};
private:
T data_;
};
int main(int argc, char **argv) {
Ex<int,4> f(1);
Ex<int,4> g(2);
Ex<int,2> h(1);
// this will fail for obvious reasons when operator double() is not defined
//
// error: cannot convert 'Ex<int, 4>' to 'double' without a conversion operator
std::cout << static_cast<double>(f) << '\n';
std::cout
// ok
<< (f == g)
// this is the error I'm supposed to get, but does not occur when I have
// operator double() defined
//
// error: invalid operands to binary expression
// ('Ex<int, 4>' and 'Ex<int, 2>')
// note: candidate function not viable: no known conversion from
// 'Ex<int, 2>' to 'const Ex<int, 4>' for 1st argument
// bool operator==(const Ex& rhs) const
<< (f == h)
<< '\n';
}
You should mark your operator double() as explicit. That allows the static cast, but prevents it being used as an implicit conversion when you test for equality (and in other cases).

Overloading = in C++

I'm trying to overload the assignment operator and would like to clear a few things up if that's ok.
I have a non member function, bool operator==( const MyClass& obj1, const myClass& obj2 ) defined oustide of my class.
I can't get at any of my private members for obvious reasons.
So what I think I need to do is to overload the assignment operator. And make assignments in the non member function.
With that said, I think I need to do the following:
use my functions and copy information using strcpy or strdup. I used strcpy.
go to the assignment operator, bool MyClass::operator=( const MyClass& obj1 );
Now we go to the function overloading (==) and assign obj2 to obj1.
I don't have a copy constructor, so I'm stuck with these:
class Class
{
private:
m_1;
m_2;
public:
..
};
void Class::Func1(char buff[]) const
{
strcpy( buff, m_1 );
return;
}
void Class::Func2(char buff[]) const
{
strcpy( buff, m_2 );
return;
}
bool Class& Class::operator=(const Class& obj)
{
if ( this != &obj ) // check for self assignment.
{
strcpy( m_1, obj.m_1 );
// do this for all other private members.
}
return *this;
}
bool operator== (const Class& obj1, const Class& obj2)
{
Class MyClass1, MyClass2;
MyClass1 = obj1;
MyClass2 = obj2;
MyClass2 = MyClass1;
// did this change anything?
// Microsofts debugger can not get this far.
return true;
}
So as you can probably tell, I'm completely lost in this overloading. Any tips? I do have a completed version overloading the same operator, only with ::, so my private members won't lose scope. I return my assignments as true and it works in main. Which is the example that I have in my book.
Will overloading the assignment operator and then preforming conversions in the operator== non member function work? Will I then be able to assign objects to each other in main after having completed that step?
You have a couple of obvious mistakes here and there is some confusion about what you are actually trying to achieve. Firstly, the assignment operator operator = is meant to copy the value from one instance to another. The return value of the assignment operator is almost always a non constant reference to the target of the copy, so that you can chain assignments:
Class & operator=(const Class &rhs)
{
// copy the members
return *this;
}
The comparison operator operator == is meant to perform a comparison of two instances. It returns a boolean true if they are equal:
boolean operator==(const Class &rhs) const
{
// check if they are equal
return something;
}
The confusion is why are you trying to copy values around, or maybe assign to the instances in the comparison operator?
Op== isn't the assignment operator. T& Op= (const T&) is.
bool operator==(const T& lhs, const T& rhs) is the operation to compare two Ts. It returns true if lhs is equal to rhs, for whatever definition of "equal" you want to code.
I am guessing that you want to compare the two objects. In that case, you can just overload the operator == in class "Class". You don't need assignment operator.
class Class
{
public:
Class(int i) : m_i(i){}
bool operator==( const Class& rhs)
{
return m_i == rhs.m_i;
}
private:
int m_i;
};
int main()
{
Class t1(10), t2(10);
bool b = (t1 == t2);
}
I am not sure whether I understood the question correctly. But if you trying to check the equality using a non-member function and can't do this only because you can't access the private members of the class, then you can declare the non-member function as a friend function and use it like this:
class Test
{
public:
Test(int i) : m_i(i){}
private:
int m_i;
friend bool operator==(Test& first, Test& second);
};
bool operator==(Test& first, Test& second)
{
return first.m_i == second.m_i;
}
int main()
{
Test t1(10), t2(10);
bool b = (t1 == t2);
}