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; }
Related
From cppreference,
If T is a derived class of some base B, then std::unique_ptr<T> is
implicitly convertible to std::unique_ptr<B>
which it obviously must be for polymorphism to work as it does with raw pointers. My question is, if a smart pointer is not generally convertible to a pointer as we can see here, then what is the mechanism used by the smart pointer to allow for runtime polymorphism? My thinking is that either in a constructor or std::make_unique<>()/std::make_shared<>() the internal pointer in the object is used for this conversion. But if these implicit conversions aren't allowed anywhere else, why don't we have to call get() when constructing our smart pointers?
As a very simple example I came up with the following test:
#include <iostream>
#include <memory>
class Base
{
public:
virtual ~Base() = default;
virtual void foo() const { std::cout << "Base foo() called." << std::endl; }
};
class Derived : public Base
{
public:
virtual void foo() const override { std::cout << "Derived foo() called." << std::endl; }
};
void bar(Base* pBase)
{
std::cout << "bar() called." << std::endl;
pBase->foo();
}
int main()
{
std::unique_ptr<Base> pObject { std::make_unique<Derived>() }; // Implicit conversion here, why no call to get()?
// bar(pObject); // Can't be converted, so we have to call get()
bar(pObject.get());
}
My question is, if a smart pointer is not generally convertible to a pointer as we can see here, then what is the mechanism used by the smart pointer to allow for runtime polymorphism?
Smart pointers are explicitly designed to make such conversion possible. As you can see in std::unique_ptr constructors documentation:
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept; (6)
this overload is created for this purpose.
This constructor only participates in overload resolution if all of
the following is true:
a) unique_ptr<U, E>::pointer is implicitly
convertible to pointer
b) U is not an array type
c) Either Deleter is
a reference type and E is the same type as D, or Deleter is not a
reference type and E is implicitly convertible to D
emphasis is mine. So as pointer to derived class is implicitly
convertible to base this constructor overload makes such conversion possible.
The returned pointer from .get() does not transfer ownership to the caller (that would be .release()).
If you use the pointer from .get() to construct another smart-pointer you will get a double-free.
So this is an error:
std::unique_ptr<Base> pObject { std::make_unique<Derived>().get() };
this works
std::unique_ptr<Base> pObject { std::make_unique<Derived>().release() };
And for shared_ptr constructing from both .get() and .release() is wrong because you could have other instances having the same shared-state that you're not tranferring. So you would potentially end up with two smart-pointers to the same pointer but with a different shared-state.
Why static_case conversion from base class to derived works inside base class, but doesn't work outside the base class
#include <iostream>
using std::cout;
class Base
{
public:
template <typename T>
int getValue() const { return static_cast<const T&>(*this).getValue(); }
};
class Derived: public Base
{
public:
Derived(int v): value(v) { }
int getValue() const { return value; }
int value;
};
class Another
{
int getValue() const { return 5; }
};
template <typename T>
void out(const Base & base) {
cout << base.getValue<T>() << '\n';
}
int main() {
Derived d(5);
Base b;
out<Derived>(d); //understandable, d has derived part.
out<Derived>(b); //don't understand, b is only base.
out<Another>(b); //compile time error
//static_cast<Derived>(b); //compile time error
}
I read this article about CRTP and stumble upon this code:
template <typename T>
class Base
{
public:
void doSomething()
{
T& derived = static_cast<T&>(*this);
use derived...
}
};
class Derived : public Base<Derived>
{
...
};
And I do not clearly understand how conversion works here too.
The static_cast conversion shall be used only if this conversion is legal. In your code you are creating an object of class Base, and you are trying to convert it to the class Derived. Luckily to you the implementation of the Derived::getValue() doesn't use any data members, and returns a value from literal. Anyway that is undefined behavior.
In case of CRTP no instance of Base class is created: only instances of the Derived are used.
Upd. Try this:
//static_cast<Derived>(b); doesn't compile
static_cast<Derived&>(b); shall compile
Upd 2. You get junk because the Derived::getValue() uses a data member (in your initial version of code data members were not used).
This is part of the rules of C++. static_cast can be used to convert a base class expression to derived class. If , at runtime, the object is not actually a base class subobject of a derived class object then it is undefined behaviour with no diagnostic required.
The first sentence of your question is incorrect, this cast can be written at any point of the code.
out<Another>() fails to compile because Another is not related by inheritance to Base.
Last cast in main() is incorrect syntactically and not equivalent to code in template, you can't upcast object to object (you can downcast through, causing type contraction). In templates above you cast references.
Derived& can be bound to Base&, static_cast have no way to check it. CRTP ensures it, because this point at storage of Derived type, *this results in reference that can be safely cast to Derived& reference object.
Reference to Another can't be bound to reference to Base, when Base is not base class of Another. In that case casting pointers or references using static_cast is illegal.
Template code is legal, in case of CRTP works because template code is instatiated where Derived is comlete enough type, i.e. where template was used. Template itself doesn't generate anything and isn't compiled, only checked for correctness.
Still, in CRTP some things won't be possible, e.g. to use inside of Base class nested type declarations from Derived class as complete types, for a simple reason : they are not complete and are not subject for forward lookup, unlike member variables and functions. If such use required, then a third type has to be defined before Base, contsining required declarations.
I ask this question following the issue I raised here.
The point is quite simple. Suppose you have two classes of this kind:
template < class Derived >
class Base {
...
operator const Derived&() const {
return static_cast< const Derived& >(*this);
}
...
};
class Specialization : public Base<Specialization> {
...
};
Then suppose you have a type conversion like this one:
template < class T >
functionCall( const Base<T>& param) {
const T & val(param);
...
}
The question is: what should be the standard conforming behavior of this conversion?
Should it be the same as const T & val(static_cast<const T &> (param) ) or should it recursively iterate until stack overflow? Notice that I obtain the first behavior compiling with GNU g++ and the second compiling with Intel icpc.
I already tried to peek at the standard (section 5.9 on static_cast and section 12.3 on conversions) but due to my lack of experience I was not able to figure out the answer.
My many thanks in advance to anybody taking the time to help me out with this.
Looking at [expr.static.cast] in n3337 (first working draft after the Standard):
2/ An lvalue of type “cv1 B,” where B is a class type, can be cast to type “reference to cv2 D,” where D is a class derived (Clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists [...]
4/ Otherwise, an expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t [..]
Therefore, I would interpret that gcc's behavior is the correct one, ie the expression:
static_cast<Derived const&>(*this)
should not invoke recursively operator Derived const& () const.
I deduce this from the presence of the Otherwise keyword which implies an ordering of the rules. The rule 2/ should be tried before the rule 4/.
The use of implicit conversion operators is not recommended. In C++11 you can add the keyword explicit not only to single argument constructors, but also to conversion operators. For C++03 code, you could use an explicitly named conversion function such as self() or down_cast().
Furthermore, you seem to be using the Base class for CRTP, i.e. to enable static polymorphism. That means that you have to know at compile-time which particular Derived class you are calling. Therefore, you should not have to use const Base& references in any public code, except to implement a CRTP interface.
In my projects, I have a class template enable_crtp:
#include <type_traits>
#include <boost/static_assert.hpp>
template
<
typename Derived
>
class enable_crtp
{
public:
const Derived& self() const
{
return down_cast(*this);
}
Derived& self()
{
return down_cast(*this);
}
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~enable_crtp()
{
// no-op
}
private:
// typedefs
typedef enable_crtp Base;
// cast a Base& to a Derived& (i.e. "down" the class hierarchy)
const Derived& down_cast(const Base& other) const
{
BOOST_STATIC_ASSERT((std::is_base_of<Base, Derived>::value));
return static_cast<const Derived&>(other);
}
// cast a Base& to a Derived& (i.e. "down" the class hierarchy)
Derived& down_cast(Base& other)
{
// write the non-const version in terms of the const version
// Effective C++ 3rd ed., Item 3 (p. 24-25)
return const_cast<Derived&>(down_cast(static_cast<const Base&>(other)));
}
};
This class is privately derived from by any CRTP base class ISomeClass like this:
template<typename Impl>
class ISomeClass
:
private enable_crtp<Impl>
{
public:
// interface to be implemented by derived class Impl
void fun1() const
{
self().do_fun1();
}
void fun2()
{
self().do_fun2()
}
protected:
~ISomeClass()
{}
};
The various derived classes can implement this interface in their own specific way like this:
class SomeImpl
:
public ISomeClass<SomeImpl>
{
public:
// structors etc.
private:
// implementation of interface ISomeClass
friend class ISomeClass<SomeImpl>;
void do_fun1() const
{
// whatever
}
void do_fun2()
{
// whatever
}
// data representation
// ...
};
Outside code calling fun1 of class SomeImpl will get delegated to the appropriate const or non-const version of self() in the class enable_crtp and after down_casting the implementation do_fun1 will be called. With a decent compiler, all the indirections should be optimized away completely.
NOTE: the protected destructors of ISomeClass and enable_crtp make the code safe against users who try to delete SomeImpl* objects through base pointers.
I've been refreshing/updating my knowledge of C++ lately, and learning about strict aliasing has made me a bit wary of casting pointers of one type to another. I know that this following code sample works in practice on my compiler, but I want to make sure that it conforms to current standards:
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*(lookupTable[i]));
}
private:
Member1 member1;
Member2 member2;
static Base Tuple::* const lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
Base Tuple<Base, Member1, Member2>::* const Tuple<Base, Member1, Member2>::lookupTable[2] = {
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member1),
reinterpret_cast<Base Tuple<Base, Member1, Member2>::*>(&Tuple::member2)
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}
Essentially, this simple tuple contains a pair of elements, each of which should derive from a common base class. The Get function returns a Base* to the member that the given index represents.
The key part that I'm wondering about is the reinterpret_casts. I know that casting from Derived Struct::* to Base Struct::* is generally a no-no, but in this case I only use the pointers-to-member-variable to get a pointer to the object. (I don't try to copy a derived object as though it were a base object, nor stuff a base object into a derived object's memory.) This works as intended on G++, and I just want to be sure that I'm not going to get bitten by any compliant compilers for doing this.
You should not use reinterpret_cast there. Actually you should not mention usage of reinterpret_cast anywhere where your goal is portability. reinterpret_cast is by definition something that has platform-specific results.
For casting a pointer to base into pointer of derived class use dynamic_cast, it will return NULL when the object pointed is not of derived class. If you are absolutely sure that the class is correct then you may use static_cast.
Use of reinterpret_cast is almost never portable. On top of that, the only valid use of pointer to member casts are the implicit cast from Type Derived::* to Type Base::* and careful uses of the static_cast from Type Base::* to Type Derived::*. Since you want to change the type of the member, not the type of the object containing members, this is neither of those.
How about putting tiny functions in that array instead of pointers to members? The following code is tested and should be entirely portable.
#include <iostream>
using namespace std;
class MyBase {
public:
virtual void DoSomething() = 0;
};
class MyDerived1 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #1" << endl;
}
};
class MyDerived2 : public MyBase {
public:
virtual void DoSomething() {
cout << "I'm #2" << endl;
}
};
template <typename Base, typename Member1, typename Member2>
struct Tuple {
public:
Base* Get(int i) {
return &(this->*lookupTable[i])();
}
private:
Member1 member1;
Member2 member2;
template <typename MemType, MemType Tuple::*member>
Base& GetMember() { return this->*member; }
typedef Base& (Tuple::*get_member_func)();
static const get_member_func lookupTable[2];
};
template <typename Base, typename Member1, typename Member2>
const typename Tuple<Base, Member1, Member2>::get_member_func
Tuple<Base, Member1, Member2>::lookupTable[2] = {
&Tuple::GetMember<Member1, &Tuple::member1>,
&Tuple::GetMember<Member2, &Tuple::member2>
};
int main() {
Tuple<MyBase, MyDerived1, MyDerived2> tuple;
tuple.Get(0)->DoSomething();
tuple.Get(1)->DoSomething();
return 0;
}
EDIT: Reference from standard. If I'm reading it right, as you don't meet either of the exceptions, what you've done is unspecified and so may or may not work on any particular compiler. There aren't any exceptions for the type of the member being related.
From 5.2.10/9 (reinterpret_cast):
An rvalue of type “pointer to member
of X of type T1” can be explicitly
converted to an rvalue of type
“pointer to member of Y of type T2” if
T1 and T2 are both function types or
both object types.66) The null member
pointer value (4.11) is converted to
the null member pointer value of the
destination type. The result of this
conversion is unspecified, except in
the following cases:
— converting an
rvalue of type “pointer to member
function” to a different pointer to
member function type and back to its
original type yields the original
pointer to member value.
— converting
an rvalue of type “pointer to data
member of X of type T1” to the type
“pointer to data member of Y of type
T2” (where the alignment requirements
of T2 are no stricter than those of
T1) and back to its original type
yields the original pointer to member
value.
C style cast is always better than reinterpret_cast.
If C style cast work, it is valid platform independently.
Always avoid reinterpret_cast.
EDITED
I mean, with reinterpret_cast you could point wrong memory address, C style cast handle all the platform related issues such as ABI, memory align, pointer size, etc.
EDITED
By the inspiration of commentators, I've read ISO/IEC 14882:2003 section 5.2.10 "Reinterpret_cast".
Of course my comprehension is limited, but it strikes me to remember why I hated reinterpret_cast in the first place.
I think, reinterpret_cast is lack of or has very limited awareness of inheritance hierarchy.
If cast operand is a instance pointer of class which has complicated inheritance hierarchy (such as ATL/COM classes), one reinterpret_cast is enough to kill you process with incomprehensible errors.
We can use C style cast with vague knowledge of actual cast operation behind. But we must really know exact detail to use reinterpret_cast safely.
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.