I feel like this one has been asked before, but I'm unable to find it on SO, nor can I find anything useful on Google. Maybe "covariant" isn't the word I'm looking for, but this concept is very similar to covariant return types on functions, so I think it's probably correct. Here's what I want to do and it gives me a compiler error:
class Base;
class Derived : public Base;
SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error
Assume those classes are fully fleshed out... I think you get the idea. It can't convert a SmartPtr<Derived> into a SmartPtr<Base> for some unclear reason. I recall that this is normal in C++ and many other languages, though at the moment I can't remember why.
My root question is: what is the best way to perform this assignment operation? Currently, I'm pulling the pointer out of the SmartPtr, explicitly upcasting it to the base type, then wrapping it in a new SmartPtr of the appropriate type (note that this is not leaking resources because our home-grown SmartPtr class uses intrusive reference counting). That's long and messy, especially when I then need to wrap the SmartPtr in yet another object... any shortcuts?
SmartPtr<Base> and SmartPtr<Derived> are two distinct instantiations of a the SmartPtr template. These new classes do not share the inheritance that Base and Derived do. Hence, your problem.
what is the best way to perform this assignment operation?
SmartPtr<Base> b = d;
Does not invoke assignment operator. This invokes the copy-ctor (the copy is elided in most cases) and is exactly as if you wrote:
SmartPtr<Base> b(d);
Provide for a copy-ctor that takes a SmartPtr<OtherType> and implement it. Same goes for the assignment operator. You will have to write out the copy-ctor and op= keeping in mind the semantics of SmartPtr.
Both the copy constructor and the assignment operator should be able to take a SmartPtr of a different type and attempt to copy the pointer from one to the other. If the types aren't compatible, the compiler will complain, and if they are compatible, you've solved your problem. Something like this:
template<class Type> class SmartPtr
{
....
template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor
template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};
Templates are not covariant, and that's good; imagine what would happen in the following case:
vector<Apple*> va;
va.push_back(new Apple);
// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!
To achieve what you are trying to do, the SmartPointer class must have a templatized constructor, that takes either another SmartPointer or a pointer of another type. You could have a look at boost::shared_ptr, which does exactly that.
template <typename T>
class SmartPointer {
T * ptr;
public:
SmartPointer(T * p) : ptr(p) {}
SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}
template <typename U>
SmartPointer(U * p) : ptr(p) {}
template <typename U>
SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}
// Do the same for operator= (even though it's not used in your example)
};
Depends on the SmartPtr class. If it has a copy constructor (or in your case, assignment operator) that takes SmartPtr<T>, where T is the type it was constructed with, then it isn't going to work, because SmartPtr<T1> is unrelated to SmartPtr<T2> even if T1 and T2 are related by inheritance.
However, if SmartPtr has a templatized copy constructor/assignment operator, with template parameter TOther, that accepts SmartPtr<TOther>, then it should work.
Assuming you have control of the SmartPtr class, the solution is to provide a templated constructor:
template <class T>
class SmartPtr
{
T *ptr;
public:
// Note that this IS NOT a copy constructor, just another constructor that takes
// a similar looking class.
template <class O>
SmartPtr(const SmartPtr<O> &src)
{
ptr = src.GetPtr();
}
// And likewise with assignment operator.
};
If the T and O types are compatible, it will work, if they aren't you'll get a compile error.
I think the easiest thing is to provide automatic conversion to another SmartPtr according to the following:
template <class T>
class SmartPtr
{
public:
SmartPtr(T *ptr) { t = ptr; }
operator T * () const { return t; }
template <class Q> operator SmartPtr<Q> () const
{ return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
T *t;
};
Note that this implementation is robust in the sense that the conversion operator template does not need to know about the semantics of the smart pointer, so reference counting does not need to replicated etc.
Related
I'm reading item 28 on smart pointers of Scott Meyers's More Effective C++ and have the following question.
A complete demonstration can be found at http://ideone.com/aKq6C0 .
A derived class pointer can be converted implicitly to a base class pointer:
class Base {};
class Derived : public Base {};
void foo(Base* b) { cout << "foo called on Base pointer" << endl;}
Derived *d = new Derived();
foo(d); //No problem
But such implicit conversion cannot happen for smart pointers, i.e. SmartPtr<Derived> cannot be implicitly converted to SmartPtr<Base>. So we use a member template for such conversions:
template<typename T>
class SmartPtr {
public:
//constructors, operator->, etc
//member template for type conversion
template<NewType>
operator SmartPtr<NewType> () {
return SmartPtr<NewType>(pointee);
}
private:
T* pointee;//the raw pointer
};
This can almost work, but it can cause ambiguity:
class Remote {};
class Base : public Remote {};
class Derived : public Base {};
void foo(const SmartPtr<Remote>& p) { cout << "remote" << endl;}
void foo(const SmartPtr<Base>& p) { cout << "base" << endl;}
SmartPtr<Derived> d(new Derived());
foo(d);//compile error: ambiguity
In this example, the compiler does not know whether it should convert d to SmartPtr<Base> or SmartPtr<Remote>, although for a raw pointer Base is apparently superior. The book says
The best we can do is to use member templates to generate conversion functions, then use casts in those cases where ambiguity results.
But how exactly do we apply cast here? foo(static_cast<SmartPtr<Base>>(d)) does not compile either. From the error message I can tell that the error comes from the use of non-const reference in the copy-constructor of SmartPtr. I'm wondering what's the correct way to make the function call.
//constructors
This is the most important part that you have omitted :)
Your cast is correct as such, it all depends on set of constructors you have. If you have one that takes non-const ref - you would get the error you mentioned, if you can just change ref to being const in copy ctor and code would compile.
It may be not very obvious, but when you call
return SmartPtr<NewType>(pointee);
you are constructing new SmartPtr<NewType> that have to accept T*, and NewType and T are different types. Most likely you only have ctor that accepts raw pointer of same type (i.e. T* for SmartPtr<T> and X* for SmartPtr<X>) so compiler is looking for another converting ctor to create your new SmartPtr and finds your copy ctor, but it can't bind new value to non const ref
Edit:
IF you are using c++11 adding move ctor would also solve your problem as it would be able to bind to rvalue
SmartPtr( SmartPtr<T>&& other): pointee(other.pointee) { other.pointee = nullptr; }
I have a class that I'm writing that will take a special type for one of its constructors that could be any type that fits my requirements. I have run into the issue that this templated constructor causes my copy and move constructors to be illegal overloads!
My class is layed out like this:
template<typename ... Types>
class myclass{
public:
myclass(const myclass &other){/* copy constructor */}
myclass(myclass &&other){/* move constructor */}
template<typename Special>
myclass(Special &&arg){/* stops copy/move implementations */}
}
How can I get around this limitation?
Constrain it.
template<typename Special,
std::enable_if_t<!std::is_same<std::decay_t<Special>, myclass>{}, int> = 0 >
myclass(Special &&arg) { /* ... */ }
Depending on your particular use case, you may also want to constrain Special further to only types that fit your requirements.
This example shows the different cases:
const myclass c1(42); // special: int
myclass c2(c1); // copy
myclass c3(c2); // special: myclass& (non const)
myclass c4(std::move(c3)); // move
Live Demo
your copy/move constructors are still legal overloads, but non const l-value has an exact match with your template constructor.
You may:
use SFINAE to forbid myclass& in the template (as in T.C's answer)
provide an other overload (with exact match):
myclass(myclass &other) : myclass(static_cast<const myclass &>(other)) {}
Demo
Suppose that A derives from B. Is there a way to forbid an implicit upcast like the one in B *x = new A() by making an explicit cast necessary, for example?
There's no way to completely prevent this conversion.
You could prevent it in most places (but not in friends or members of A) using private inheritance, or by replacing inheritance with a containment or aggregation relation (where A contains an instance of or pointer/reference to B, rather then inheriting). Then a member function could simulate an explicit cast:
B * x = new A(); // not allowed
A * a = new A(); // OK
B * b = a->to_B(); // OK
But you should think about what behaviour you actually want to prevent, rather than making a perfectly normal operation require jumping through a rather odd hoop. There's almost certainly a better way to solve your specific problem.
That is not possible and against the rules of polymorphism.
Since A is derived from B, A is a B. So a cast is not needed, B is the mother class of A. Any instance of A can be changed to an instance of B.
That is what you want basically because it allows for programming towards interfaces.
Usually the interface, although interfaces don't exist in C++ , is defined via some kind of abstract class. The code is usually in a better shape if you are able to keep referring to the interface and keep away from the actual implementation of a certain interface.
You may create a wrapper class for that, something like:
template<typename T>
class MyPtr
{
public:
MyPtr(T* t) : t(t) {}
template <typename U>
MyPtr(U*) = delete;
MyPtr operator = (T* t) { this->t = t; return *this; }
template <typename U>
MyPtr operator = (U* t) = delete;
operator T* () const { return t; }
private:
T* t;
};
Live example
I've been working on a way to prevent user of using a class without smart pointers. Thus, forcing them to have the object being heap allocated and managed by smart pointers.
In order to get such a result, I've tried the following :
#include <memory>
class A
{
private :
~A {}
// To force use of A only with std::unique_ptr
friend std::default_delete<A>;
};
This work pretty well if you only want your class users being capable of manipulating instance of your class through std::unique_ptr. But it doesn't work for std::shared_ptr. So I'd like to know if you had any ideas to get such a behavior. The only solution that I've found is doing the following (using friend std::allocator_traits<A>; was unsufficient) :
#include <memory>
class A
{
private :
~A {}
// For std::shared_ptr use with g++
friend __gnu_cxx::new_allocator<A>;
};
But this solution is not portable. Maybe I'm doing it the wrong way.
Create a friend'd factory function that returns a std::unique_ptr<A>, and make your class have no accessible constructors. But make the destructor available:
#include <memory>
class A;
template <class ...Args>
std::unique_ptr<A> make_A(Args&& ...args);
class A
{
public:
~A() = default;
private :
A() = default;
A(const A&) = delete;
A& operator=(const A&) = delete;
template <class ...Args>
friend std::unique_ptr<A> make_A(Args&& ...args)
{
return std::unique_ptr<A>(new A(std::forward<Args>(args)...));
}
};
Now your clients can obviously get a unique_ptr<A>:
std::unique_ptr<A> p1 = make_A();
But your clients can just as easily get a shared_ptr<A>:
std::shared_ptr<A> p2 = make_A();
Because std::shared_ptr can be constructed from a std::unique_ptr. And if you have any user-written smart pointers, all they have to do to be interoperable with your system is create a constructor that takes a std::unique_ptr, just like std::shared_ptr has, and this is very easy to do:
template <class T>
class my_smart_ptr
{
T* ptr_;
public:
my_smart_ptr(std::unique_ptr<T> p)
: ptr_(p.release())
{
}
// ...
};
As there is no general term "smart pointer" what you want is not possible.
What you can do, is supporting some known set of smart pointers. The usual solution starts as yours, making ctor or dtor private, and adds factory functions. That can return the instance packed with your desired smart pointers. If you just want to support unique_ptr and shared_ptr, that meaans two factory functions, hardly too much. (note that those pointers allow smuggling out the raw pointer through simple interface so the control is not full.)
With the code:
template <typename T>
class MyClass
{
public:
MyClass(const MyClass& other)
{
//Explicit, types must both match;
}
template <typename U>
MyClass(const MyClass<U>& other)
{
//Implicit, types can copy across if assignable.
//<?> Is there a way to make this automatically happen for memberwise
//copying without having to define the contents of this function?
this->value = other.getValue();
}
privte:
T value;
};
The following cases would be true:
MyClass<float> a;
MyClass<float> b = a; //Calls explicit constructor which would have been auto-generated.
MyClass<int> c;
MyClass<float> d = c; //Calls implicit constructor which would not have been auto gen.
My question is: is there a way to get an implicit copy constructor like this, but not have to define how it works? Copy, assignment, and standard constructors are all provided when a class is created by default... it would be nice to be able to get an implicit conversion constructor as well for free if possible when using templates.
I'm guessing its not possible but can someone explain why that is to me if that is the case?
Thanks!
You can't make the compiler generate a pseudo-copy-constructor-template for you. Why? Because MyClass<T> can be something entirely different from MyClass<U>
This isn't my original answer. I had misunderstood the question, sorry