Why isn't there a std::shared_ptr T[] specialisation?
The LWG (Library Working Group of the C++ committee) briefly considered the possibility but the idea wasn't without controversy. Though the controversy was mainly about a feature added to the shared_ptr<T[]>
proposal that could have been jettisoned (arithmetic on shared_ptr<T[]>
).
But ultimately the real real reason is that though it was discussed, there was never an actual written proposal in front of the LWG to do this. It never bubbled up anyone's priority list (including my own) sufficiently to put the time into writing a proposal.
Informal conversations have recently begun anew on this topic among a few LWG members, and I have personally prototyped it. But there is still no written proposal for it. I think it would be a decent additional tool in the toolbox. Whether it will ever actually happen or not is anyone's guess.
Update
Array support for shared_ptr
now has a draft TS:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4077.html
Update (2017)
This is now supported in C++17. See case 3 of shared_ptr::shared_ptr()
Why does std::shared_ptr T = std::unique_ptr T[] compile, while std::shared_ptr T[] = std::unique_ptr T[] does not?
§20.8.2.2.1/28:
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
Remark: This constructor shall not participate in overload resolution
unlessunique_ptr<Y, D>::pointer
is convertible toT*
.
However, unique_ptr<U[]>::pointer
is actually U*
, while shared_ptr<U[]>
's T*
is U(*)[]
; And U*
cannot be converted to U(*)[]
, hence the overload is never considered.
Why is there no [] operator for std::shared_ptr?
std::unique_ptr
only defines operator[]
in a specialization for arrays: std::unique_ptr<T[]>
. For non-array pointers, the operator[] doesn't make much sense anyways (only [0]
).
Such a specialization for std::shared_ptr
is missing (in C++11), which is discussed in the related question: Why isn't there a std::shared_ptr<T[]> specialisation?
You should not use a non-array smart pointer with array allocation, unless you provide a custom deleter. In particular, unique_ptr<int> p = new int[10]
is bad, since it calls delete
instead of delete[]
. Use unique_ptr<int[]>
instead, which calls delete[]
. (And this one implements operator[]
). If you're using shared_ptr
to hold a T[]
, you need to use a custom deleter. See also shared_ptr to an array : should it be used? -- but it doesn't provide operator[]
, since it uses type erasure to distinguish between array and non-array (the smart pointer type is independent of the provided deleter).
If you wonder why there is no shared_ptr
specialization for arrays: that was a proposal, but wasn't included in the standard (mainly since you can work around by writing ptr.get() + i
for ptr[i]
).
Problems with shared_ptr T[] wrapping a dynamic array
The solution you suggest is possible, but you will lose the size of the array:
#include <memory>
#include <cstddef>
using namespace std;
template<typename T> shared_ptr<T> make_shared_array(size_t size)
{
return shared_ptr<T>(new T[size], default_delete<T[]>());
}
struct Foo
{
shared_ptr<char> field;
};
int main()
{
Foo a;
a.field = make_shared_array<char>(256);
return 0;
}
What I have done here is to let the array decay into a pointer. As long as the deleter is an array deleter it should behave correctly.
To prevent this loss of size, and if you cannot use boost::shared_array as suggested, I would suggest to encapsulate this information in your own shared_array class.
Conditionally specialize std::hash for std::shared_ptr struct
The problem with the code in your question is not that it is a redefinition, but that default template arguments are not allowed in partial specializations and that all template parameters in a partial specialization must be deducible.
std::hash
does not provide a second template parameter in the primary template that could be used for SFINAE, but based on this answer you could do something like this as a workaround:
#include <memory>
#include <utility>
class Base {};
class Derived : public Base {};
template <typename First, typename... Others>
using first = First;
namespace std {
template <typename T>
struct hash<first<std::shared_ptr<T>,
std::enable_if_t<std::is_base_of_v<Base, T>>>> {
size_t operator()(const std::shared_ptr<T>& d) const { return 616; }
};
} // namespace std
which I would assume is ok in principle, because the declaration depends a user-defined type Base
.
There is an unresolved issue in the standard regarding whether or not this specialization should be considered a redefinition of the standard's specialization for std::shared_ptr
. (GCC thinks it isn't, Clang thinks it is.)
But more importantly, then you still have the problem that this partial specialization is not more specialized than the one that the standard provides for std::shared_ptr
. Therefore any actual use would result in an ambiguity error and I don't think there is any way to make the specialization more specialized.
Thus I think your only solution is to define an explicit specialization of std::hash
for each derived type, maybe with the help of a macro. Or alternatively (and probably more appropriately) you should write your own hasher functor and provide that as alternative to std::hash
where it is needed.
Check for null in std::shared_ptr
Most shared pointers are exactly like normal pointers in this
respect. You have to check for null. Depending on the
function, you may want to switch to using
void myFunction( Foo const& foo );
, and calling it by dereferencing the pointer (which pushes the
responsibility for ensuring that the pointer is not null to the
caller).
Also, it's probably bad practice to make the function take
a shared pointer unless there are some special ownership
semantics involved. If the function is just going to use the
pointer for the duration of the function, neither changing it or
taking ownership, a raw pointer is probably more appropriate,
since it imposes less constraints on the caller. (But this
really depends a lot on what the function does, and why you are
using shared pointers. And of course, the fact that you've
passed a non-const reference to the shared pointer supposes that
you are going to modify it, so passing a shared pointer might be
appropriate.)
Finally, different implementations of shared pointers make it
more or less difficult to check for null. With C++11, you can
use std::shared_ptr
, and just compare it to nullptr
naturally, as you'd expect. The Boost implementation is a bit
broken in this respect, however; you cannot just compare it to0
or NULL
. You must either construct an emptyboost::shared_ptr
for the comparison, or call get
on it and
compare the resulting raw pointer to 0
or NULL
.
Wrap dynamic array with shared_ptr by make_shared
I recommend you use std::vector<unsigned char> vec(512);
, wrapping contiguous dynamic arrays is exactly what it's for. Getting the raw buffer pointer is as simple as vec.data()
;
If the vector needs to be shared, than you can still use a smart pointer
auto p_vec = make_shared<vector<unsigned char>>(512);
You'll get the benefit of reference counting with virtually no overhead due to using vector, and you'll get the entire vector API.
Why is `make_unique T[N] ` disallowed?
Quoting from the original proposal:
T[N]
As of N3485,
unique_ptr
doesn't provide a partial specialization forT[N]
.
However, users will be strongly tempted to writemake_unique<T[N]>()
. This
is a no-win scenario. Returningunique_ptr<T[N]>
would select the primary
template for single objects, which is bizarre. Returningunique_ptr<T[]>
would be an exception to the otherwise ironclad rule that
make_unique<something>()
returnsunique_ptr<something>
. Therefore, this
proposal makesT[N]
ill-formed here, allowing implementations to emit
helpfulstatic_assert
messages.
The author of the proposal, Stephan T. Lavavej, illustrates this situation in this video on Core C++ (courtesy of chris), starting from minute 1:01:10 (more or less).
SmartPointers and pointer to array
You are probably asking for
std::shared_ptr<std::array<BYTE,100>> pointer(make_shared<std::array<BYTE,100>>());
LPBYTE old_pointer = pointer.get()->data();
To have another reference you can simply have a statement like this
// increases reference count
std::shared_ptr<std::array<BYTE,100>> pointer2 = pointer;
See the detailed documentations here please: std::shared_ptr
constructor and std::shared_ptr::get()
.
Though remember:
Accessing the smart pointer's pointee using std::shared_ptr::get()
bypasses the semantics and features provided by using the std::shared_ptr
. That's highly discouraged, unless you very well know what you're doing with old_pointer
, and if it's still in scope.
Related Topics
Rand() % 14 Only Generates the Values 6 or 13
Copy Constructor For a Class With Unique_Ptr
Resolution of Std::Chrono::High_Resolution_Clock Doesn't Correspond to Measurements
How to Generate a Calling Graph For C++ Code
Understanding of Pthread_Cond_Wait() and Pthread_Cond_Signal()
Why Is the C++ Initializer_List Behavior For Std::Vector and Std::Array Different
Sort List Using Stl Sort Function
How to Get the Error Message from the Error Code Returned by Getlasterror()
How to Read a Cmake Variable in C++ Source Code
Global Variable Within Multiple Files
Increasing Accuracy of Solution of Transcendental Equation
C/C++ Check If One Bit Is Set In, I.E. Int Variable
Why Is C++11'S Pod "Standard Layout" Definition the Way It Is
Why Would One Use Nested Classes in C++