Reputation: 3567
I don't see a reason why these don't have an assignment operator overload for plain old pointers of the type they're templated to. If the goal of making smart pointers interface as close to plain old pointers as they could, then why didn't they make an overload for the assignment operator like this?
inline std::shared_ptr<type> &operator=( const type * pointer)
{
reset(a);
}
this way you could use them just like you would a normal pointer, like so:
std::shared_ptr<int> test = new int;
it's not an issue at all, just wondering why they went to the trouble of just overloading a couple of operators.
Also wondering if there's a way to overload the global assignment operator to do this, or if there's any reason i shouldn't.
edit: adding a response to Nawaz about his answer here for code formatting. I just wrote this test program to see if what you were saying was right:
template<class T>
class peh
{
public:
peh() {meh = 3;}
const peh<T> & operator=(const int * peh)
{
}
};
void f( peh<int> teh)
{
}
int main()
{
int * meh = new int;
f(meh);
system("PAUSE");
return 0;
}
this here errors out saying there is no usable conversion from peh<int>
to int *
. so why is it acceptable with std::shared_ptr<int>
to int *
?
Upvotes: 2
Views: 3261
Reputation: 88155
The operator=
you show would not actually enabled the syntax you want. shared_ptr<int> p = new int;
would use shared_ptr's constructor from T* and shared_ptr's copy constructor. shared_ptr has both of these, but your syntax does not work because the constructor from T* is explicit
.
The reason for this is because if that construction, std::shared_ptr<int> test = new int;
, could be done implicitly it would mean that a shared_ptr could take ownership of a pointer without anyone ever explicitly asking it to. Nawaz shows one reason this would be really error prone; you'd have to be really careful that a pointer you're using isn't suddenly adopted by a shared_ptr somewhere without your knowledge, and then destroyed out from under you.
Here's an example that shows this dangerous implicit construction:
#include <iostream>
template<typename T>
struct owning_pointer {
T *t;
owning_pointer(T *t) : t{t} {}
~owning_pointer() {
std::cout << t << " deleted\n";
delete t;
}
};
void foo(owning_pointer<int> thief) {}
int main() {
int *i = new int;
std::cout << i << " allocated\n";
foo(i);
}
The output will be something like:
0x10d400880 allocated
0x10d400880 deleted
And see the error you get when you add explicit
to owning_ptr's constructor. I get:
main.cpp:18:5: error: no matching function for call to 'foo'
foo(i);
^~~
main.cpp:13:6: note: candidate function not viable: no known conversion from 'int *' to 'owning_pointer<int>' for 1st argument;
void foo(owning_pointer<int> thief) {}
^
Also it's unnecessary to allow implicit construction from T* since there are already some perfectly simple ways to allocate without the same potential for errors:
std::shared_ptr<int> test(new int); // one extra character isn't a hardship. I typically prefer () construction anyway.
std::shared_ptr<int> test{new int}; // although I might start preferring {} construction in C++11
auto test = std::make_shared<int>(); // this is slightly different in that the allocated int is zero-initialized
If you're initializing a member shared_ptr then you can initialize it in the initializer list instead of using assignment or reset()
in the body of the constructor:
struct foo {
std::shared_ptr<int> m_meh;
foo()
: m_meh(new int)
{
// no need for m_meh.reset(new int) here
}
};
What operator=
would enable is this:
shared_ptr<int> s;
s = new int;
This doesn't seem quite as error prone as implicit construction of shared_ptr from T*, but I can't see that there's really any value to it either.
Upvotes: 2
Reputation: 361442
Also wondering if there's a way to overload the global assignment operator to do this, or if there's any reason i shouldn't.
No. Assignment operator overload must a member function.
By the way, if you want the following functionality, then you should not talk about assignment operator, you should rather ask : why the constructor which takes raw pointer as argument is made explicit
? why it is not implicit?
//this code requires an implicit constructor, not assignment!
std::shared_ptr<int> test = new int; //illegal
It is illegal, but suppose for a while that this was allowed, then you would be able to call the following function passing a raw pointer as argument : such a feature would be dangerous, as the rest of the answer explains (read the comments) :
void f(std::shared_ptr<int> test)
{
//code
} //test will be destructed here (when it goes out of scope)
//if test.use_count() == 1, then the pointer which it manages
//will be destructed as well. (NOTE THIS POINT)
Now see the dangerous part:
int *ptr = new int;
f(ptr);
//note that calling f is allowed if it is allowed:
//std::shared_ptr<int> test = new int;
//it is as if ptr is assigned to the parameter:
//std::shared_ptr<int> test = ptr;
//Question : now what happened in f()?
//Answer : inside f(), test (the shared_ptr) will infer that no one else
//refers to the pointer it contains, because test.use_count() == 1
//test is obviously wrong in this case, because it cannot prove that!
//DANGER
*ptr = 10; //undefined behavior, because ptr is deleted by the shared_ptr
Please read the comments. It explains each part of the code snippet above.
Upvotes: 5