boost.png (6897 bytes)make_shared and allocate_shared for arrays

Introduction
Synopsis
Common Requirements
Free Functions
History
References

Introduction

Originally the Boost function templates make_shared and allocate_shared were for efficient allocation of shared objects only. There was a need to have efficient allocation of shared arrays. One criticism of class template shared_array was always the lack of a make_shared utility which ensures only a single allocation.

The header files <boost/smart_ptr/make_shared_array.hpp> and <boost/smart_ptr/allocate_shared_array.hpp> provide function templates, overloads of make_shared and allocate_shared for array types, to address this need. make_shared uses the global operator new to allocate memory, whereas allocate_shared uses an user-supplied allocator, allowing finer control.

Synopsis

namespace boost {
    template<class U> // U is T[]
    shared_ptr<U> make_shared(size_t size);

    template<class U, class A> // U is T[]
    shared_ptr<U> allocate_shared(const A& allocator, size_t size);

    template<class U> // U is T[N]
    shared_ptr<U> make_shared();

    template<class U, class A> // U is T[N]
    shared_ptr<U> allocate_shared(const A& allocator);

    template<class U> // U is T[]
    shared_ptr<U> make_shared(size_t size, const T& value);

    template<class U, class A>  // U is T[]
    shared_ptr<U> allocate_shared(const A& allocator, size_t size, const T& value);

    template<class U> // U is T[N]
    shared_ptr<U> make_shared(const T& value);

    template<class U, class A> // U is T[N]
    shared_ptr<U> allocate_shared(const A& allocator, const T& value);

    template<class U> // U is T[]
    shared_ptr<U> make_shared_noinit(size_t size);

    template<class U, class A> // U is T[]
    shared_ptr<U> allocate_shared_noinit(const A& allocator, size_t size);

    template<class U> // U is T[N]
    shared_ptr<U> make_shared_noinit();

    template<class U, class A> // U is T[N]
    shared_ptr<U> allocate_shared_noinit(const A& allocator);
}

Common Requirements

template<class U>
    shared_ptr<U> make_shared(args);
template<class U, class A>
    shared_ptr<U> allocate_shared(const A& allocator, args);
template<class U>
    shared_ptr<U> make_shared_noinit(args);
template<class U, class A>
    shared_ptr<U> allocate_shared_noinit(const A& allocator, args);

Requires: U is of the form T[] or T[N]. A shall be an Allocator, as described in section 17.6.3.5 [Allocator requirements] of the C++ Standard. The copy constructor and destructor of A shall not throw exceptions.

Effects: Allocates memory for an object of type U (or T[size] when U is T[], where size is determined from args as specified by the concrete overload). The object is initialized as specified by the concrete overload. The templates allocate_shared and allocate_shared_noinit use a copy of allocator to allocate memory. If an exception is thrown, the functions have no effect.

Returns: A shared_ptr instance that stores and owns the address of the newly constructed object.

Postconditions: r.get() != 0 && r.use_count() == 1, where r is the return value.

Throws: bad_alloc, an exception thrown from A::allocate, or from the initialization of the object.

Remarks:

This implementation performs no more than one memory allocation. This provides efficiency to equivalent to an intrusive smart pointer.

When an object of an array type T is specified to be initialized to a value of the same type value, this shall be interpreted to mean that each array element of the object is initialized to the corresponding element from value.

When an object of an array type is specified to be value-initialized, this shall be interpreted to mean that each array element of the object is value-initialized.

Array elements are initialized in ascending order of their addresses.

When a subobject of a non-array type T is specified to be initialized to a value value, make_shared shall perform this initialization via the expression ::new(ptr) T(value), where ptr has type void* and points to storage suitable to hold an object of type T.

When a subobject of non-array type T is specified to be initialized to a value value, allocate_shared shall perform this initialization via the expression allocator_traits<A2>::construct(a2, ptr, value), where ptr points to storage suitable to hold an object of type T and a2 of type A2 is a rebound copy of the allocator allocator passed to allocate_shared such that its value_type is T.

When a subobject of non-array type T is specified to be value-initialized, make_shared shall perform this initialization via the expression ::new(ptr) T(), or ::new(ptr) T(hdl), where ptr has type void* and points to storage suitable to hold an object of type T, and hdl is an instace of shared_handle.

When a subobject of non-array type T is specified to be value-initialized, allocate_shared shall perform this initialization via the expression allocator_traits<A2>::construct(a2, ptr) or, allocator_traits<A2>::construct(a2, hdl, ptr) where ptr points to storage suitable to hold an object of type T and a2 of type A2 is a rebound copy of the allocator allocator passed to allocate_shared such that its value_type is T.

When a subobject of non-array type T is specified to be default-initialized, make_shared_noinit and allocate_shared_noinit shall perform this initialization via the expression ::new(ptr) T, where ptr has type void* and points to storage suitable to hold an object of type T.

When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction.

Notes: These functions will typically allocate more memory than sizeof(U) to allow for internal bookkeeping structures such as the reference counts.

Free Functions

template<class U> 
    shared_ptr<U> make_shared(size_t size);
template<class U, class A> 
    shared_ptr<U> allocate_shared(const A& allocator, size_t size);

Returns: A shared_ptr to a value-initialized object of type T[size].

Remarks: These overloads shall only participate in overload resolution when U is of the form T[].

Examples:

boost::shared_ptr<int[]> a1 = boost::make_shared<int[]>(size);
boost::shared_ptr<int[][2]> a2 = boost::make_shared<int[][2]>(size);
template<class U> 
    shared_ptr<U> make_shared();
template<class U, class A> 
    shared_ptr<U> allocate_shared(const A& allocator);

Returns: A shared_ptr to a value-initialized object of type T[N].

Remarks: These overloads shall only participate in overload resolution when U is of the form T[N].

Examples:

boost::shared_ptr<int[8]> a1 = boost::make_shared<int[8]>();
boost::shared_ptr<int[4][2]> a2 = boost::make_shared<int[4][2]>();
template<class U> 
    shared_ptr<U> make_shared(size_t size, const T& value);
template<class U, class A> 
    shared_ptr<U> allocate_shared(const A& allocator, size_t size, const T& value);

Returns: A shared_ptr to an object of type T[size], where each array element of type T is initialized to value.

Remarks: These overloads shall only participate in overload resolution when U is of the form T[].

Examples:

boost::shared_ptr<int[]> a1 = boost::make_shared<int[]>(size, 1);
boost::shared_ptr<int[][2]> a2 = boost::make_shared<int[][2]>(size, {1, 2});
template<class U> 
    shared_ptr<U> make_shared(const T& value);
template<class U, class A> 
    shared_ptr<U> allocate_shared(const A& allocator, const T& value);

Returns: A shared_ptr to an object of type T[N], where each array element of type T is initialized to value.

Remarks: These overloads shall only participate in overload resolution when U is of the form T[N].

Examples:

boost::shared_ptr<int[8]> a1 = boost::make_shared<int[8]>(1);
boost::shared_ptr<int[4][2]> a2 = boost::make_shared<int[4][2]>({1, 2});
template<class U> 
    shared_ptr<U> make_shared_noinit(size_t size);
template<class U, class A> 
    shared_ptr<U> allocate_shared_noinit(const A& allocator, size_t size);

Returns: A shared_ptr to a default-initialized object of type T[size].

Remarks: These overloads shall only participate in overload resolution when U is of the form T[].

Examples:

boost::shared_ptr<int[]> a1 = boost::make_shared_noinit<int[]>(size);
boost::shared_ptr<int[][2]> a2 = boost::make_shared_noinit<int[][2]>(size);
template<class U> 
    shared_ptr<U> make_shared_noinit();
template<class U, class A> 
    shared_ptr<U> allocate_shared_noinit(const A& allocator);

Returns: A shared_ptr to a default-initialized object of type T[N].

Remarks: These overloads shall only participate in overload resolution when U is of the form T[N].

Examples:

boost::shared_ptr<int[8]> a1 = boost::make_shared_noinit<int[8]>();
boost::shared_ptr<int[4][2]> a2 = boost::make_shared_noinit<int[4][2]>();

History

February 2014. Glen Fernandes updated overloads of make_shared and allocate_shared to conform to the specification in C++ standard paper N3870, including resolving C++ standard library defect report 2070, and reduced the spatial overhead of the internal bookkeeping structures.

November 2012. Glen Fernandes contributed implementations of make_shared and allocate_shared for arrays.

References

N3870, Extending make_shared to Support Arrays, Revision 1, Peter Dimov & Glen Fernandes, January, 2014.


$Date$

Copyright 2012-2014 Glen Fernandes. Distributed under the Boost Software License, Version 1.0. See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.