How to declare simple conversion operator T() in templated base class - c++

I've been working on some class hierarchy with a goal to have a collection of classes that encapsulate primitive types (AdvancedInt, AdvanceDouble, etc.) and give them some additional behavior when accessing their underlaying values, through conversion operator and assignment operator. I can't do this with getters and setter as I can't change the original code and the way it access them, so my only choice is operator overloading.
The problem I ran into is with the templated class that defines the conversion operator which gives me a compiler error.
Here is the code that illustrates the problem
#include <string>
class Base {
public:
Base() = delete;
Base(std::string s) : name(s) {
//Some extra logic
}
~Base() {
//More custom logic
}
virtual std::string get_name() const final { //Final on purpose
return name;
}
private:
const std::string name;
};
template <typename T, class C> class Serializable :public Base { //Poor man's interface (but not quite)
public:
Serializable() = delete;
Serializable(std::string name) : Base(name) {
}
operator T() const {
//Some extra 'getter' logic
return value;
}
C& operator=(const T& val) {
//Some extra 'setter' logic
value = val;
return (C&)*this;
}
virtual void serialize() = 0; //This has to be pure virtual
private:
T value;
};
class AdvancedInt final : public Serializable<int, AdvancedInt> { //This is the actual complete class
public:
AdvancedInt(std::string name) : Serializable(name) {
//Nothing here, but needed for the special constructor logic from AbstractBase
}
void serialize() override {
//Some logic here
}
};
int main()
{
AdvancedInt adv{"foo"};
int calc = adv;
calc += 7;
adv = calc; //error C2679 (msvc) | binary '=': no operator found which takes a right-hand operand of type 'int' (or there is no acceptable conversion)
return 0;
}
Now aside from the (probably) questionable practice of passing AdvancedInt to the template of its own base class and it subsequently knowing that it's part of some derived class and implementing its logic (although any feedback on this is welcome), my main question is, what am I doing wrong here?
The C& operator=(const T& val) compiles fine when defined as pure virtual, but that enforces the implementation to be in the derived class, and when implemented there with T and C replaced by their corresponding types it works just fine, so the signature is logically sound, but probably not in the correct place(?). From what I've found on cppreference.com, the simple assignment operator is the only assignment operator that cannot be defined outside of class definition. Is this the reason why this code doesn't compile? And if so, is declaring it as pure virtual a way to go?

With adv = calc we look for operator= from AdvancedInt and found (implicit):
AdvancedInt& operator=(const AdvancedInt&);
AdvancedInt& operator=(AdvancedInt&&);
and look-up stops there.
Adding:
using Serializable<int, AdvancedInt>::operator=;
would allow to consider also that overload.
And that fixes your issue.
Demo.

Related

Make method explicit except for friend classes

I am trying to create a conversion operator that would be explicit by default, except for some designated classes.
More precisely, I have a relatively simple class template whose instances should be convertible to some other type (int throughout this question, for simplicity). However, I want this conversion to be explicit by default, but still allow it to be implicit for one other class, namely the class passed as template argument. Without this last part, this is what it would look like:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
};
Now, I want to be able to write something like A a(2); int i = a; inside the methods of T (which is supposed to be a class).
My first idea was to make use of friend declarations and declare a private overload of the conversion operator. However, overloading based solely on explicit isn't allowed. So I tried using const for that instead, and it worked:
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() { // Public non-const
return i;
}
private:
int i;
operator int() const { // Private const
return i;
}
friend T;
};
... until it doesn't. This works only when using non-const As, and while I don't plan on using const As, I still would like it to work in all cases. Note that if the public overload is const and the private one isn't, this works only using const As.
Here is a demo showing in which cases it works or doesn't.
I have thought of using volatile (demo), and while it makes it better, it still leads to the same problem: it only works when using non-volatile As.
Is there a better way to do that? More generally, is there a way to solve the problem in the very first sentence?
There is apparently no satisfying solution. The volatile option seems to be the most practical, and although it doesn't quite work with volatile As, an additional const_cast solves that problem.
template<typename T>
class A {
public:
A(int i) : i(i) {}
explicit operator int() const {
return i;
}
private:
int i;
operator int() const volatile {
return static_cast<int>(const_cast<A const&>(*this));
// or just
// return i;
}
friend T;
};
Demo
As Sneftel stated in the comments, access permissions tend to be granted on a pull basis (using) rather than a push basis. That might be why the language does not provide a way to change conversion semantics depending on the class or block performing the conversions.

Virtual-like friend functions?

I want to create interface like
class Scalar {
public:
Scalar() {}
virtual ~Scalar() {}
//virtual members operators
virtual Scalar& operator+() const = 0;
virtual const Scalar operator-() const;
virtual Scalar& operator=() = 0;
virtual Scalar& operator+=() = 0;
//...
};
I intend also to use some friend functions, for example:
friend const Scalar operator+(const Scalar&, const Scalar&);
But there is a problem when I derive the abstract class, and create derived class, say:
class RealNumber: public Scalar {
public:
friend const RealNumber operator+(const RealNumber&, const RealNumber&);
//some definitions...
};
According to this logic, I would need to define a new overload of friend operator+ for every new class derived from Scalar. Is there some way to solve this problem and avoid declaring these friends in all the derived classes?
Is this your problem ?
I understand that your problem is that your two friends refer to totally different functions, since they have a different signature:
friend const Scalar operator+(const Scalar&, const Scalar&);
friend const RealNumber operator+(const RealNumber&, const RealNumber&);
Worse, the choice of a class external friend will not be polymorphic: the right friend will be chose based on the compile-time type.
How to solve it ?
First of all, instead of using an outside overloaded friend, you could consider overriding the operator of the class itself (keeping the signature identical).
However this has two major challenges:
it is almost impossible to return a reference from arithmetic operator, unless you'd accept side-effects, which would then make your operator behave differently than expected.
you'd need to cope with combining different kind of scalars: what if you'd have to add a Scalar to a RealNumber ? This would require a double dispatch to be implemented to cope with all the possible combination.
So, dead-end ?
No, there are two other alternatives, depending on your real problem:
Do you really want to combine arithmetic type dynamically at run-time ? If yes, you need to go away from the C++ operator overriding approach, and implement an expression evaluator, using the interpreter pattern.
If not, consider to use a template based design, so that the compiler choses at compile-time the appropriate specialisation.
Or suffer with the many possible friends and their combination of parameter types.
You may not be able to create virtual friend functions, but you can create virtual operators (even operator + could be done this way).
Consider the following code: WARNING: THIS IS NOT GOOD DESIGN AT ALL
#include <iostream>
using namespace std;
class AA {
private:
int a;
public:
AA(int a): a(a) {};
inline int getA() const { return a; };
virtual AA operator +(const AA &a) {
AA res(this->getA() + a.getA());
return res;
}
};
class BB: public AA {
public:
BB(int a): AA(a) {}
virtual AA operator +(const AA &a) {
AA res(this->getA() - a.getA());
return res;
}
};
int main() {
BB tmp(1);
AA& a = tmp;
AA b(7);
cout << (a + b).getA();
return 0;
}
When I was writing this code, I found that a lot of flaws could be induced (like the + operator that really does substraction instead, also what if the second operand was BB instead of the first one ??)
So, about your problem, you want Scalars. So you can do the following approach:
#include <iostream>
using namespace std;
// Here, Scalar acts as an abstract class, all its goal is to convert your
// class type into some calculable value type (e.g. you can use T as double)
template <typename T>
class Scalar {
public:
// Converter function is abstract
virtual operator T() = 0;
};
class AA: public Scalar<double> {
private:
double a;
public:
inline double getA() {return a;};
AA(double a): a(a) {}
// Implements the converter function in Scalar, T in scalar is mapped
// to double because we did Scalar<double>
virtual operator double() {
return a;
}
};
class BB: public Scalar<double> {
private:
int a;
public:
inline double getA() {return (double)a;};
BB(int a): a(a) {}
virtual operator double() {
return (double)a;
}
};
int main() {
BB tmp(1);
AA b(7);
// Here, it is easy for us just to add those, they are automatically converted into doubles, each one like how it is programmed.
cout << (b + tmp);
return 0;
}

copy and swap idiom with pure virtual class

I am trying to implement virtual class with pure virtual method and 'copy and swap' idiom, but I've encountered some problems. Code won't compile because I am creating instance in the assign operator of the class A which contains pure virtual method.
Is there a way how to use pure virtual method and copy and swap idiom?
class A
{
public:
A( string name) :
m_name(name) { m_type = ""; }
A( const A & rec) :
m_name(rec.m_name), m_type(rec.m_type) {}
friend void swap(A & lhs, A & rhs)
{
std::swap(lhs.m_name, rhs.m_name);
std::swap(lhs.m_type, rhs.m_type);
}
A & operator=( const A & rhs)
{
A tmp(rhs);
swap(*this, tmp);
return *this;
}
friend ostream & operator<<( ostream & os,A & x)
{
x.print(os);
return os;
}
protected:
virtual void print(ostream & os) =0;
string m_type;
string m_name;
};
class B : A
{
public:
B(string name, int att) :
A(name),
m_att(att)
{
m_type="B";
}
B( const B & rec) :
A(rec),
m_att(rec.m_att) {}
friend void swap(B & lhs, B & rhs)
{
std::swap(lhs.m_att, rhs.m_att);
}
B & operator=( const B & rec)
{
B tmp(rec) ;
swap(*this, tmp);
return *this;
}
private:
virtual void print(ostream & os);
int m_att;
};
Error message:
In member function ‘A& A::operator=(const A&)’:|
error: cannot declare variable ‘tmp’ to be of abstract type ‘A’|
because the following virtual functions are pure within ‘A’:|
virtual void A::print(std::ostream&)|
As your compiler informs you, you cannot create a variable of abstract type. There is no way of dancing around that.
This leaves you three main options:
Stop using pure virtual functions
First, you could just get rid of the pure virtual methods and provide a little stub in each of them that calls std::terminate, which would obviously break compile time detection of whether all (former) pure virtual methods are overridden in all derived classes.
This will cause slicing, since it will only copy the base class and everything that makes out the derived class is lost.
Use a stub class w/o pure virtual functions
Similar to that, you could create a derived class that implements all virtual methods with simple stubs (possibly calling std::terminate), and is used only used as a "instantiatable version of the base class".
The most important part to implement for this class would be a constructor that takes a const reference to the base class, so you can just use it instead of copying the base class. This example also adds a move constructor, because I am a performance fetishist.
This causes the same slicing problem as the first option. This may be your intended result, based on what you are doing.
struct InstantiatableA : public A {
InstantiatableA(A const& rhs) : A(rhs) { }
InstantiatableA(A&& rhs) : A(::std::move(rhs)) { }
void print(ostream&) override { ::std::terminate(); }
};
A& A::operator=(InstantiatableA rhs) {
using ::std::swap;
swap(*this, rhs);
return *this;
}
Note: This is really a variable of type A, although I said it could not be done. The only thing you have to be aware is that the variable of type A lives inside a variable of type InstantiatableA!
Use a copy factory
Finally, you can add a virtual A* copy() = 0; to the base class. Your derived class B will then have to implement it as A* copy() override { return new B(*this); }. The reason dynamic memory is necessary is because your derived types may require arbitrarily more memory than your base class.
You're just facing the fact that inheritance works awkwardly with copy semantics.
For instance, imagine you found a trick to pass the compiling phase, what would mean (following example uses assignment but the issue is the same with a copy) :
// class A
// a class B : public A
// another class C : public A inheriting publicly from A
// another class D : public B inheriting publicly from B
B b1;
C c1;
D d1;
// Which semantic for following valid construction when copy/assignment is defined in A ?
b1 = c1;
b1 = d1;
A &ra = b1;
B b2;
// Which semantic for following valid construction when copy/assignment is defined in A ?
ra = b2;
ra = c1;
ra = d1;
CRTP is a choice:
template<typename swappable>
struct copy_swap_crtp{
auto& operator=(copy_swap_crtp const& rhs){
if (this==std::addressof(tmp))
return *this;
swappable tmp{rhs.self()};
self().swap(tmp);
return *this;
};
auto& operator=(copy_swap_crtp && rhs){
self().swap(rhs.self());
return *this;
};
protected:
auto& self(){return *static_cast<swappable*>(this);};
//auto& self()const{return *static_cast<swappable const*>(this);};
};
user class:
struct copy_swap_class
: copy_swap_crtp<copy_swap_class>
{
copy_swap_class(copy_swap_class const&);
void swap(copy_swap_class&);
};
cheers,
FM.
The compiler is right. The class A is abstract class, therefore you can not create instances of it in the operator=.
In B, you just declared the print function, but you didn't implement it. Meaning, you will get linking errors.
By implementing it, it compiles fine (if we ignore various warnings) :
void B::print(ostream & os )
{
os << m_att;
}
by the way :
B inherits privately from A, is that what you wanted?
order of initialization in A's copy constructor is wrong
you initialized m_type in A's constructor's body and not in the initialization list

Overloading operator+

I'm looking for an elegant solution to the following "problem":
Consider the classes Base and Child, and the way operator+ works here:
class Base
{
public:
Base(int a=0) : mValue(a) {};
Base(const Base& rhs) : mValue(rhs.mValue) {};
Base& operator=(const Base& rhs) {
if (this==&rhs) return *this;
mValue=rhs.mValue;
}
friend const Base operator+(Base &lhs, Base &rhs);
private:
int mValue;
};
const Base operator+(Base &lhs, Base &rhs)
{
Base result(lhs.mValue+rhs.mValue);
return result;
}
class Child : public Base
{
public:
Child(int a=0) : Base(a) {};
};
int main()
{
Child a(2);
Child b(5);
Child c(a+b); // **** This line ****
Child d;
d=(a+b); // **** or this other one ****
}
The marked lines in main give the error:
cannot convert from 'const Base' to 'Child'
I understand perfectly that the operator has been defined in the Base class, and returns an object of type Base, which can't be converted to Child.
One solution is overloading operator+ for the Child class, but I am wondering whether there is a better, less costly method. I'm under the impression that I'm forgetting a much easier option. Thanks!
you can define a constructor Child(Base& obj) then Child c=(a+b); statement will be fine and then you can use the base object as per your requirement.
There is no easier option.
Operator overloading and class hierarchies don't really like to mix. I'm personally very suspicious when a type that should be a value type (or else why are you overloading operators?) is part of a hierarchy. Can you describe your actual architecture? Does it actually make sense to overload the operator there?
If you want to build a child from a base without adding the appropriate constructor, there is another alternative. You can declare the casting operator in your base class, returning your mValue :
operator int() const;
This way, the compiler will do the job implicitly.
PS : Ensure const-correctness of your additive operator :
friend Base operator+(const Base &lhs, const Base &rhs);

Overloading baseclass assignment operator in subclass class leads to ambiguous assignment error

I have this base class (details removed)
template<class T>
class GPtr
{
public:
typedef T BaseType;
GPtr& operator=(const BaseType& rhs)
{
m_p = rhs.get();
return *this;
}
private:
BaseType m_p;
};
Then a sub-class specialises the template and adds another assignment option:
class GDrawablePtr : public GPtr<XYZ>
{
public:
GDrawablePtr& operator=(const RootType& rhs)
{
GPtr::operator =(convert<BaseType::element_type>(rhs));
return *this;
}
/* -- only compiles if this is uncommented
GDrawablePtr& operator=(const BaseType& rhs)
{
GPtr::operator =(rhs);
return *this;
}
*/
};
With that code commented out, I get compilation errors about ambiguous assignment when assigning instances. If I uncomment it, then even though it doesn't appear to do anything new, compilation is successful.
Is there a way to avoid re-defining the original base assignment operator, and what is the reason for this behaviour?
It's known as hiding: declaring a function in a derived class makes any function in the base class with the same name inaccessible. You can use a using-declaration to make the base class versions available too:
// In GDrawablePtr
using GPtr::operator=;