Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/memory/shared ptr/pointer cast"

From cppreference.com
< cpp‎ | memory‎ | shared ptr
m (Possible implementation: ~title; fmt)
m ({{c}}, @-@ -> @,@)
 
(2 intermediate revisions by 2 users not shown)
Line 3: Line 3:
 
{{dcl begin}}
 
{{dcl begin}}
 
{{dcl header|memory}}
 
{{dcl header|memory}}
{{dcl|num=1|since=c++11|1=
+
{{dcla|num=1|since=c++11|1=
 
template< class T, class U >
 
template< class T, class U >
 
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
 
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
Line 11: Line 11:
 
std::shared_ptr<T> static_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
std::shared_ptr<T> static_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
}}
 
}}
{{dcl|num=3|since=c++11|1=
+
{{dcla|num=3|since=c++11|1=
 
template< class T, class U >
 
template< class T, class U >
 
std::shared_ptr<T> dynamic_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
 
std::shared_ptr<T> dynamic_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
Line 19: Line 19:
 
std::shared_ptr<T> dynamic_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
std::shared_ptr<T> dynamic_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
}}
 
}}
{{dcl|num=5|since=c++11|1=
+
{{dcla|num=5|since=c++11|1=
 
template< class T, class U >
 
template< class T, class U >
 
std::shared_ptr<T> const_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
 
std::shared_ptr<T> const_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
Line 27: Line 27:
 
std::shared_ptr<T> const_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
std::shared_ptr<T> const_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
 
}}
 
}}
{{dcl|num=7|since=c++17|1=
+
{{dcla|num=7|since=c++17|1=
 
template< class T, class U >
 
template< class T, class U >
 
std::shared_ptr<T> reinterpret_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
 
std::shared_ptr<T> reinterpret_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
Line 36: Line 36:
 
}}
 
}}
 
{{dcl end}}
 
{{dcl end}}
Creates a new instance of {{lc|std::shared_ptr}} whose stored pointer is obtained from {{tt|r}}'s stored pointer using a cast expression.
+
Creates a new instance of {{lc|std::shared_ptr}} whose stored pointer is obtained from {{c|r}}'s stored pointer using a cast expression.
  
If {{tt|r}} is empty, so is the new {{tt|shared_ptr}} (but its stored pointer is not necessarily null). Otherwise, the new {{tt|shared_ptr}} will share ownership with the initial value of {{tt|r}}, except that it is empty if the {{tt|dynamic_cast}} performed by {{tt|dynamic_pointer_cast}} returns a null pointer.
+
If {{c|r}} is empty, so is the new {{tt|shared_ptr}} (but its stored pointer is not necessarily null). Otherwise, the new {{tt|shared_ptr}} will share ownership with the initial value of {{c|r}}, except that it is empty if the {{tt|dynamic_cast}} performed by {{tt|dynamic_pointer_cast}} returns a null pointer.
  
 
Let {{tt|Y}} be {{c|typename std::shared_ptr<T>::element_type}}, then the resulting {{lc|std::shared_ptr}}'s stored pointer will be obtained by evaluating, respectively:
 
Let {{tt|Y}} be {{c|typename std::shared_ptr<T>::element_type}}, then the resulting {{lc|std::shared_ptr}}'s stored pointer will be obtained by evaluating, respectively:
  
@1-2@ {{c|static_cast<Y*>(r.get())}}.
+
@1,2@ {{c|static_cast<Y*>(r.get())}}
@3-4@ {{c|dynamic_cast<Y*>(r.get())}} (If the result of the {{tt|dynamic_cast}} is a null pointer value, the returned {{tt|shared_ptr}} will be empty.)
+
@3,4@ {{c|dynamic_cast<Y*>(r.get())}}. If the result of the {{tt|dynamic_cast}} is a null pointer value, the returned {{tt|shared_ptr}} will be empty.
@5-6@ {{c|const_cast<Y*>(r.get())}}.
+
@5,6@ {{c|const_cast<Y*>(r.get())}}
@7-8@ {{c|reinterpret_cast<Y*>(r.get())}}
+
@7,8@ {{c|reinterpret_cast<Y*>(r.get())}}
  
 
The behavior of these functions is undefined unless the corresponding cast from {{tt|U*}} to {{tt|T*}} is well formed:
 
The behavior of these functions is undefined unless the corresponding cast from {{tt|U*}} to {{tt|T*}} is well formed:
  
@1-2@ The behavior is undefined unless {{c|static_cast<T*>((U*)nullptr)}} is well formed.
+
@1,2@ The behavior is undefined unless {{c|static_cast<T*>((U*)nullptr)}} is well formed.
@3-4@ The behavior is undefined unless {{c|dynamic_cast<T*>((U*)nullptr)}} is well formed.
+
@3,4@ The behavior is undefined unless {{c|dynamic_cast<T*>((U*)nullptr)}} is well formed.
@5-6@ The behavior is undefined unless {{c|const_cast<T*>((U*)nullptr)}} is well formed.
+
@5,6@ The behavior is undefined unless {{c|const_cast<T*>((U*)nullptr)}} is well formed.
@7-8@ The behavior is undefined unless {{c|reinterpret_cast<T*>((U*)nullptr)}} is well formed.
+
@7,8@ The behavior is undefined unless {{c|reinterpret_cast<T*>((U*)nullptr)}} is well formed.
  
 
{{rrev|since=c++20|
 
{{rrev|since=c++20|
After calling the rvalue overloads {{v|2,4,6,8}}, {{tt|r}} is empty and {{c|r.get() {{==}} nullptr}}, except that {{tt|r}} is not modified for {{tt|dynamic_pointer_cast}} {{v|4}} if the {{c|dynamic_cast}} fails.
+
After calling the rvalue overloads {{v|2,4,6,8}}, {{c|r}} is empty and {{c|r.get() {{==}} nullptr}}, except that {{c|r}} is not modified for {{tt|dynamic_pointer_cast}} {{v|4}} if the {{c|dynamic_cast}} fails.
 
}}
 
}}
 +
 
===Parameters===
 
===Parameters===
 
{{par begin}}
 
{{par begin}}
{{par|r|The pointer to convert}}
+
{{par|r|the pointer to convert}}
 
{{par end}}
 
{{par end}}
  
Line 67: Line 68:
 
===Possible implementation===
 
===Possible implementation===
 
{{eq impl
 
{{eq impl
|1=
+
|title1=static_pointer_cast|ver1=1|1=
template< class T, class U >
+
template<class T, class U>
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept
+
std::shared_ptr<T> static_pointer_cast(const std::shared_ptr<U>& r) noexcept
 
{
 
{
 
     auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
 
     auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
 
     return std::shared_ptr<T>{r, p};
 
     return std::shared_ptr<T>{r, p};
 
}
 
}
|title2=Third version|2=
+
|title2=dynamic_pointer_cast|ver2=3|2=
template< class T, class U >
+
template<class T, class U>
std::shared_ptr<T> dynamic_pointer_cast( const std::shared_ptr<U>& r ) noexcept
+
std::shared_ptr<T> dynamic_pointer_cast(const std::shared_ptr<U>& r) noexcept
 
{
 
{
 
     if (auto p = dynamic_cast<typename std::shared_ptr<T>::element_type*>(r.get()))
 
     if (auto p = dynamic_cast<typename std::shared_ptr<T>::element_type*>(r.get()))
Line 83: Line 84:
 
         return std::shared_ptr<T>{};
 
         return std::shared_ptr<T>{};
 
}
 
}
|title3=Fifth version|3=
+
|title3=const_pointer_cast|ver3=5|3=
template< class T, class U >
+
template<class T, class U>
std::shared_ptr<T> const_pointer_cast( const std::shared_ptr<U>& r ) noexcept
+
std::shared_ptr<T> const_pointer_cast(const std::shared_ptr<U>& r) noexcept
 
{
 
{
 
     auto p = const_cast<typename std::shared_ptr<T>::element_type*>(r.get());
 
     auto p = const_cast<typename std::shared_ptr<T>::element_type*>(r.get());
 
     return std::shared_ptr<T>{r, p};
 
     return std::shared_ptr<T>{r, p};
 
}
 
}
|title4=Seventh version|4=
+
|title4=reinterpret_pointer_cast|ver4=7|4=
template< class T, class U >
+
template<class T, class U>
std::shared_ptr<T> reinterpret_pointer_cast( const std::shared_ptr<U>& r ) noexcept
+
std::shared_ptr<T> reinterpret_pointer_cast(const std::shared_ptr<U>& r) noexcept
 
{
 
{
 
     auto p = reinterpret_cast<typename std::shared_ptr<T>::element_type*>(r.get());
 
     auto p = reinterpret_cast<typename std::shared_ptr<T>::element_type*>(r.get());
Line 101: Line 102:
 
===Example===
 
===Example===
 
{{example
 
{{example
|
 
 
|code=
 
|code=
 
#include <iostream>
 
#include <iostream>
Line 110: Line 110:
 
public:
 
public:
 
     int a;
 
     int a;
     virtual void f() const { std::cout << "I am base!\n";}
+
     virtual void f() const { std::cout << "I am base!\n"; }
     virtual ~Base(){}
+
     virtual ~Base() {}
 
};
 
};
 
   
 
   
Line 118: Line 118:
 
public:
 
public:
 
     void f() const override { std::cout << "I am derived!\n"; }
 
     void f() const override { std::cout << "I am derived!\n"; }
     ~Derived(){}
+
     ~Derived() {}
 
};
 
};
  
int main(){
+
int main()
 +
{
 
     auto basePtr = std::make_shared<Base>();
 
     auto basePtr = std::make_shared<Base>();
 
     std::cout << "Base pointer says: ";
 
     std::cout << "Base pointer says: ";
Line 137: Line 138:
 
     // dynamic_pointer_cast to go down/across class hierarchy
 
     // dynamic_pointer_cast to go down/across class hierarchy
 
     auto downcastedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
 
     auto downcastedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
     if(downcastedPtr)
+
     if (downcastedPtr)
 
     {
 
     {
 
         std::cout << "Downcasted pointer says: ";
 
         std::cout << "Downcasted pointer says: ";
Line 145: Line 146:
 
     // All pointers to derived share ownership
 
     // All pointers to derived share ownership
 
     std::cout << "Pointers to underlying derived: "
 
     std::cout << "Pointers to underlying derived: "
            << derivedPtr.use_count()
+
              << derivedPtr.use_count()
            << "\n";
+
              << '\n';
 
}
 
}
 
 
|output=
 
|output=
 
Base pointer says: I am base!
 
Base pointer says: I am base!

Latest revision as of 06:58, 7 October 2023

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
Dynamic memory management
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Allocators
Garbage collection support
(C++11)(until C++23)
(C++11)(until C++23)
(C++11)(until C++23)
(C++11)(until C++23)
(C++11)(until C++23)
(C++11)(until C++23)



 
 
Defined in header <memory>
template< class T, class U >
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
(1) (since C++11)
template< class T, class U >
std::shared_ptr<T> static_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
(2) (since C++20)
template< class T, class U >
std::shared_ptr<T> dynamic_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
(3) (since C++11)
template< class T, class U >
std::shared_ptr<T> dynamic_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
(4) (since C++20)
template< class T, class U >
std::shared_ptr<T> const_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
(5) (since C++11)
template< class T, class U >
std::shared_ptr<T> const_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
(6) (since C++20)
template< class T, class U >
std::shared_ptr<T> reinterpret_pointer_cast( const std::shared_ptr<U>& r ) noexcept;
(7) (since C++17)
template< class T, class U >
std::shared_ptr<T> reinterpret_pointer_cast( std::shared_ptr<U>&& r ) noexcept;
(8) (since C++20)

Creates a new instance of std::shared_ptr whose stored pointer is obtained from r's stored pointer using a cast expression.

If r is empty, so is the new shared_ptr (but its stored pointer is not necessarily null). Otherwise, the new shared_ptr will share ownership with the initial value of r, except that it is empty if the dynamic_cast performed by dynamic_pointer_cast returns a null pointer.

Let Y be typename std::shared_ptr<T>::element_type, then the resulting std::shared_ptr's stored pointer will be obtained by evaluating, respectively:

1,2) static_cast<Y*>(r.get())
3,4) dynamic_cast<Y*>(r.get()). If the result of the dynamic_cast is a null pointer value, the returned shared_ptr will be empty.
5,6) const_cast<Y*>(r.get())
7,8) reinterpret_cast<Y*>(r.get())

The behavior of these functions is undefined unless the corresponding cast from U* to T* is well formed:

1,2) The behavior is undefined unless static_cast<T*>((U*)nullptr) is well formed.
3,4) The behavior is undefined unless dynamic_cast<T*>((U*)nullptr) is well formed.
5,6) The behavior is undefined unless const_cast<T*>((U*)nullptr) is well formed.
7,8) The behavior is undefined unless reinterpret_cast<T*>((U*)nullptr) is well formed.

After calling the rvalue overloads (2,4,6,8), r is empty and r.get() == nullptr, except that r is not modified for dynamic_pointer_cast (4) if the dynamic_cast fails.

(since C++20)

Contents

[edit] Parameters

r - the pointer to convert

[edit] Notes

The expressions std::shared_ptr<T>(static_cast<T*>(r.get())), std::shared_ptr<T>(dynamic_cast<T*>(r.get())) and std::shared_ptr<T>(const_cast<T*>(r.get())) might seem to have the same effect, but they all will likely result in undefined behavior, attempting to delete the same object twice!

[edit] Possible implementation

static_pointer_cast
template<class T, class U>
std::shared_ptr<T> static_pointer_cast(const std::shared_ptr<U>& r) noexcept
{
    auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
    return std::shared_ptr<T>{r, p};
}
dynamic_pointer_cast
template<class T, class U>
std::shared_ptr<T> dynamic_pointer_cast(const std::shared_ptr<U>& r) noexcept
{
    if (auto p = dynamic_cast<typename std::shared_ptr<T>::element_type*>(r.get()))
        return std::shared_ptr<T>{r, p};
    else
        return std::shared_ptr<T>{};
}
const_pointer_cast
template<class T, class U>
std::shared_ptr<T> const_pointer_cast(const std::shared_ptr<U>& r) noexcept
{
    auto p = const_cast<typename std::shared_ptr<T>::element_type*>(r.get());
    return std::shared_ptr<T>{r, p};
}
reinterpret_pointer_cast
template<class T, class U>
std::shared_ptr<T> reinterpret_pointer_cast(const std::shared_ptr<U>& r) noexcept
{
    auto p = reinterpret_cast<typename std::shared_ptr<T>::element_type*>(r.get());
    return std::shared_ptr<T>{r, p};
}

[edit] Example

#include <iostream>
#include <memory>
 
class Base
{
public:
    int a;
    virtual void f() const { std::cout << "I am base!\n"; }
    virtual ~Base() {}
};
 
class Derived : public Base
{
public:
    void f() const override { std::cout << "I am derived!\n"; }
    ~Derived() {}
};
 
int main()
{
    auto basePtr = std::make_shared<Base>();
    std::cout << "Base pointer says: ";
    basePtr->f();
 
    auto derivedPtr = std::make_shared<Derived>();
    std::cout << "Derived pointer says: ";
    derivedPtr->f();
 
    // static_pointer_cast to go up class hierarchy
    basePtr = std::static_pointer_cast<Base>(derivedPtr);
    std::cout << "Base pointer to derived says: ";
    basePtr->f();
 
    // dynamic_pointer_cast to go down/across class hierarchy
    auto downcastedPtr = std::dynamic_pointer_cast<Derived>(basePtr);
    if (downcastedPtr)
    {
        std::cout << "Downcasted pointer says: ";
        downcastedPtr->f();
    }
 
    // All pointers to derived share ownership
    std::cout << "Pointers to underlying derived: "
              << derivedPtr.use_count()
              << '\n';
}

Output:

Base pointer says: I am base!
Derived pointer says: I am derived!
Base pointer to derived says: I am derived!
Downcasted pointer says: I am derived!
Pointers to underlying derived: 3

[edit] See also

constructs new shared_ptr
(public member function) [edit]