std::make_shared, std::make_shared_for_overwrite
Defined in header <memory>
.
Declarations
// 1)
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args ); // T is not array
C++20
// 2)
template< class T >
shared_ptr<T> make_shared( std::size_t N ); // T is U[]
// 3)
template< class T >
shared_ptr<T> make_shared(); // T is U[N]
// 4)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u ); // T is U[]
// 5)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u ); // T is U[N]
// 6)
template< class T >
shared_ptr<T> make_shared_for_overwrite(); // T is not U[]
// 7)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N ); // T is U[]
Constructs an object of type T
and wraps it in a std::shared_ptr
using args as the parameter list for the constructor of T
. The object is constructed as if by
the expression ::new (pv) T(std::forward<Args>(args)...)
, where pv
is an internal void*
pointer to storage suitable to hold an object of type T
.
The storage is typically larger than sizeof(T)
in order to use one allocation for both the control block of the shared pointer and the T
object.
The std::shared_ptr
constructor called by this function enables shared_from_this
with a pointer to the newly constructed object of type T
.
This overload participates in overload resolution only if T
is not an array type .
2,3)
Same as (1), but the object constructed is a possibly-multidimensional array whose non-array elements of type std::remove_all_extents_t<T>
are value-initialized as
if by placement-new expression ::new(pv) std::remove_all_extents_t<T>()
. The overload (2) creates an array of size N
along the first dimension.
The array elements are initialized in ascending order of their addresses, and when their lifetime ends are destroyed in the reverse order of their original construction.
4,5)
Same as (2,3), but every element is initialized from the default value u
. If U
is not an array type, then this is performed as if by the same placement-new expression as in (1);
otherwise, this is performed as if by initializing every non-array element of the (possibly multidimensional) array with the corresponding element from u
with the
same placement-new expression as in (1). The overload (4) creates an array of size N
along the first dimension. The array elements are initialized in ascending order
of their addresses, and when their lifetime ends are destroyed in the reverse order of their original construction.
6)
Same as (1) if T
is not an array type and (3) if T
is U[N]
, except that the created object is default-initialized.
7)
Same as (2), except that the individual array elements are default-initialized.
In each case, the object (or individual elements if
Tis an array type)
will be destroyed by p->~X()
, where p
is a pointer to the object and X
is its type.
Parameters
args
- list of arguments with which an instance of T
will be constructed.
N
- array size to use
u
- the initial value to initialize every element of the array
Return value
std::shared_ptr
of an instance of type T
.
Exceptions
May throw std::bad_alloc
or any exception thrown by the constructor of T
. If an exception is thrown, the functions have no effect.
If an exception is thrown during the construction of the array, already-initialized elements are destroyed in reverse order .
Notes
This function may be used as an alternative to std::shared_ptr<T>(new T(args...))
. The trade-offs are:
std::shared_ptr<T>(new T(args...))
performs at least two allocations (one for the objectT
and one for the control block of the shared pointer), whilestd::make_shared<T>
typically performs only one allocation (the standard recommends, but does not require this; all known implementations do this)- If any
std::weak_ptr
references the control block created bystd::make_shared
after the lifetime of all shared owners ended, the memory occupied byT
persists until all weak owners get destroyed as well, which may be undesirable ifsizeof(T)
is large. std::shared_ptr<T>(new T(args...))
may call a non-public constructor of T if executed in context where it is accessible, whilestd::make_shared
requires public access to the selected constructor.- Unlike the
std::shared_ptr
constructors,std::make_shared
does not allow a custom deleter. std::make_shared
uses::new
, so if any special behavior has been set up using a class-specific operator new, it will differ fromstd::shared_ptr<T>(new T(args...))
.
Until C++20
std::shared_ptr
supports array types (as of C++17), butstd::make_shared
does not. This functionality is supported byboost::make_shared
Until C++17
- code such as
f(std::shared_ptr<int>(new int(42)), g())
can cause a memory leak ifg
gets called afternew int(42)
and throws an exception, whilef(std::make_shared<int>(42), g())
is safe, since two function calls are never interleaved.
A constructor enables shared_from_this
with a pointer ptr of type U*
means that it determines if U
has an unambiguous and accessible (since C++17)
base class that is a specialization of std::enable_shared_from_this
, and if so, the constructor evaluates the statement:
if (ptr != nullptr && ptr->weak_this.expired())
ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>(
*this, const_cast<std::remove_cv_t<U>*>(ptr));
Where weak_this
is the hidden mutable std::weak_ptr
member of std::enable_shared_from_this
. The assignment to the weak_this
member is not atomic and
conflicts with any potentially concurrent access to the same object. This ensures that future calls to shared_from_this()
would share ownership with the
std::shared_ptr
created by this raw pointer constructor.
The test ptr->weak_this.expired()
in the exposition code above makes sure that weak_this
is not reassigned if it already indicates an owner.
This test is required as of C++17.
Feature-test macro | Value | Std | Comment |
---|---|---|---|
__cpp_lib_shared_ptr_arrays | 201707L | (C++20) | Array support of std::make_shared, for overloads (2-5) |
__cpp_lib_smart_ptr_for_overwrite | 202002L | (C++20) | Smart pointer creation with default initialization, for overloads (6,7) |
Example
#include <memory>
#include <vector>
#include <iostream>
#include <type_traits>
struct C
{
// constructors needed (until C++20)
C(int i) : i(i) {}
C(int i, float f) : i(i), f(f) {}
int i;
float f{};
};
int main()
{
// using `auto` for the type of `sp1`
auto sp1 = std::make_shared<C>(1); // overload (1)
static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
// being explicit with the type of `sp2`
std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // overload (1)
static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
// shared_ptr to a value-initialized float[64]; overload (2):
std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
// shared_ptr to a value-initialized long[5][3][4]; overload (2):
std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
// shared_ptr to a value-initialized short[128]; overload (3):
std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
// shared_ptr to a value-initialized int[7][6][5]; overload (3):
std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
// shared_ptr to a double[256], where each element is 2.0; overload (4):
std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
// shared_ptr to a double[7][2], where each double[2]
// element is {3.0, 4.0}; overload (4):
std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
// shared_ptr to a vector<int>[4], where each vector
// has contents {5, 6}; overload (4):
std::shared_ptr<std::vector<int>[]> sp9 =
std::make_shared<std::vector<int>[]>(4, {5, 6});
// shared_ptr to a float[512], where each element is 1.0; overload (5):
std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
// shared_ptr to a double[6][2], where each double[2] element
// is {1.0, 2.0}; overload (5):
std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
// shared_ptr to a vector<int>[4], where each vector
// has contents {5, 6}; overload (5):
std::shared_ptr<std::vector<int>[4]> spC =
std::make_shared<std::vector<int>[4]>({5, 6});
}
sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }