unique_ptr and template

Quick question:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <vector>
#include <memory>
struct Base {
    virtual void f() { std::cout << "Base\n"; }
    virtual ~Base(){}
    
    int x = 45;
};
struct Derived : Base {
    void f() { std::cout << "Derived\n"; }
    
    int y = 67;
};


int main()
{
    std::vector<std::unique_ptr<Base>> v;
    std::vector<Base*> V1;
    
    V1.emplace_back(new Derived);
    V1.emplace_back(new Base);

    v.emplace_back(new Derived);
    v.emplace_back(new Base);
    
    std::cout << v[0]->x << " then " << v[1]->x << "then " << dynamic_cast<std::unique_ptr<Derived>>(v[1])->y;// NOT OK
    std::cout << V1[0]->x << " then " << V1[1]->x << "then " << dynamic_cast<Derived*>(V1[1])->y;// OK
}


I've copied this example from elsewhere and tinkered some of my own and wanted to know how come the above code doesnt compile?

For the unique_ptr case it tells me that the v[1] objecs is a std::vector object, not a unique_ptr to Base.

I compiled on IDEone.com using C++11 lang.

What am i missing about unique_ptr?
Don't cast from unique<T> to unique<U>, cast the pointer returned from .get()
Thanks for the quick reply.

something like this?

1
2
 
std::cout << v[0]->x << " then " << v[1]->x << "then " << dynamic_cast<Derived*>(v[1].get())->y;


.get() will return the raw pointer to Base is that correct?

How come the compiled sees the bad v[1] as a std::vector of unique pointers to base? and not a unique_ptr to Base?
Yes, does that not work?

tomkulaga wrote:
How come the compiled sees the bad v[1] as a std::vector of unique pointers to base? and not a unique_ptr to Base?
I have no idea what you mean by this?
Yup that works perfect.

Compilation error	 time: 0 memory: 3432 signal:0
prog.cpp: In function ‘int main()’:
prog.cpp:29:112: error: cannot dynamic_cast ‘(& v.std::vector<_Tp, _Alloc>::operator[]<std::unique_ptr<Base>, std::allocator<std::unique_ptr<Base> > >(0u))->std::unique_ptr<_Tp, 
_Dp>::get<Base, std::default_delete<Base> >()’ (of type ‘std::unique_ptr<Base>::pointer {aka struct Base*}’) to type ‘class std::unique_ptr<Derived>’ (target is not pointer or reference)
     std::cout << v[0]->x << " then " << v[1]->x << "then " << dynamic_cast<std::unique_ptr<Derived>>(v[0].get())->y;  


Apologies for the wideness, thats the message I'm getting, my question is why does the orignal one error and fundamentally why cannot i cast from unique<T> to unique<U>?

Thanks
You can't cast from unique<T> to unique<U> because unique<U> doesn't define a constructor to allow it and unique<T> doesn't provide a cast operator to allow it. For templates in general this is true, but especially for smart pointer classes like unique_ptr you will definitely not want more than one instance thinking it owns a pointer.

Remember, even if the wrapped types in two containers are compatible, the two containers themselves may not be compatible if they don't provide convenience casting support. I am guessing you're use to Java's Generics? They're nothing like C++ Templates, even though they look the same.
Last edited on
Thanks for that. Would an std::move be used somehow?

I was confused with the wrapped typed influencing the container type, but as you said they don't.

would this work with a shared_ptr, out of curiosity?

Nah not used to Java just trying out some of the new C++11 features and was hoping that would naively work. Now trying to figure out why, so I an understand it.

How does the dynamic_cast work? What goes on behind the scenes?

Thanks for your replies.
dynamic_cast doesn't care about what's in the objects you're casting, it only cares about the inheritance relationship between them. There is no inheritance relationship between unique<T> and unique<U>, in fact they're completely independent classes even though they came from the same template, so there's no way to cast between them.

And you wouldn't want a std::move to happen, because then your original unique_ptr would no longer refer to an object, and what would the rest of your code do? Let the greedy guy take it and leave?
Ahh yes, dynamic only goes between inherited types, overlooked that. Would a reinterpret cast naively do it?
No, because the internal pointer would not have the dynamic_cast magic performed on it (the address might need to be changed).
A dynamic cast on a pointer results in a new pointer being created; for instance std::dynamic_pointer_cast<> on a std::shared_ptr<> returns a new instance of std::shared_ptr<>.
http://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

std::unique_ptr<> holds the sole ownership of a managed object. Therefore, two different std::unique_ptr<> instances can't manage the same object; and dynamic cast semantics will not work with unique pointers.

We can cast the reference to the object held by a std::unique_ptr<> though - creation of a new unique pointer is not required. If the cast succeeds, we get a reference (with a different static type) to the same object; if it fails, an exception is thrown.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <memory>
#include <type_traits>

template< typename T, typename U >
typename std::enable_if< std::is_polymorphic<U>::value, T& >::type
dynamic_deref( const std::unique_ptr<U>& p )
{
    if(p) return dynamic_cast<T&>( *p ) ;
    throw std::invalid_argument( "nullptr" ) ;
}

struct A { virtual ~A() {} } ;
struct B : virtual A {} ;
struct C : virtual A { C( int v ) : c(v) {} const int c ; } ;
struct D : B, C { D( int i ) : C(i) {} } ;

int main()
{

    try
    {
        std::unique_ptr<A> pa( new D(99) ) ;
        std::cout << "try down-cast A => C: " ;
        C& rc = dynamic_deref<C>(pa) ;
        std::cout << "ok. rc.c == " << rc.c << '\n' ;

        std::unique_ptr<B> pb( new D(1234) ) ;
        std::cout << "try cross-cast B => C: " ;
        C& rc1 = dynamic_deref<C>(pb) ;
        std::cout << " ok. rc1.c == " << rc1.c << '\n' ;

        pb.reset( new B ) ;
        std::cout << "try cross-cast B => C: " ;
        C& rc2 = dynamic_deref<C>(pb) ;
        std::cout << " ok. rc2.c == " << rc2.c << '\n' ;
    }

    catch( const std::exception& e )
    {
        std::cout << "error: " << e.what() << '\n' ;

        try
        {
            std::unique_ptr<A> pa ;
            std::cout << "try down-cast A => C: " ;
            C& rc = dynamic_deref<C>(pa) ;
            std::cout << " ok. rc.c == " << rc.c << '\n' ;
        }

        catch( const std::exception& e )
        {
            std::cout << "error: " << e.what() << '\n' ;
        }
    }
}

http://coliru.stacked-crooked.com/a/fa9305713f767d74
Topic archived. No new replies allowed.