Many smart pointer classes overload the address-of operator &
to give you access to the inner raw pointer.
Unfortunately, they disagree on what happens to the object being managed by the smart pointer before you get its raw address.
Library | Existing contents |
---|---|
_com_ptr_t | Released |
ATL (CComPtr) | Must be empty (will assert in Debug) |
MFC (IPTR) | Released |
WRL (ComPtr) | Released |
wil (com_ptr) | Released |
C++/WinRT (com_ptr) | N/A |
C++/WinRT avoids the confusion by simply not having an overloaded operator&
at all! Not having an overloaded operator&
also makes it easier to take the address of the smart pointer itself. The put()
method Releases any managed COM pointer and then returns the address of the raw pointer.
So let’s finish the table. Let’s say that sp
is the name of a variable of the corresponding smart pointer type.
Library | Release | Don’t release | Assumes empty |
---|---|---|---|
_com_ptr_t | &sp |
&sp.GetInterfacePtr() |
|
ATL (CComPtr) | &sp.p |
&sp |
|
MFC (IPTR) | &sp |
||
WRL (ComPtr) | &sp p.ReleaseAndGetAddressOf() |
p.GetAddressOf() |
|
wil (com_ptr) | &sp sp.put() |
sp.addressof() |
|
C++/WinRT (com_ptr) | sp.put() |
Bonus chatter: The possibility of an overloaded operator&
is one of those special cases you tend to forget about when writing template library code.¹ In general, it’s not safe to use the &
operator to get the address of an object of unknown type, because the operator might be overloaded. You have to use std::addressof
.
¹ Hey, at least it’s not an overloaded comma operator. That thing is nasty.
I recall a time this caused great sadness. There was a custom smart pointer type that had the release-on-& behaviour. It was accessed in a fashion like this:
spLinkedList->Next(&spLinkedList);
Which, if it's not obvious how that breaks, can be thought of as similar to a C function with the variable left of the "->" being passed as a magic first parameter 'this' pointer:
CPlusPlusNameMangling_Next(spLinkedList, &spLinkedList)
Order of evaluation of arguments is undefined in C++. So there's two possible results. In our original compiler, it evaluated left-to-right. That meant we passed the variable as the this pointer, then we released...
Isn’t null-pointer dereferencing a case of UB on C++? Is AV really guaranteed, at least in the expected order? I remember Raymond’s articles on UB discussing that.
Wouldn’t something like
spLinkedList->Next(spLinkedList.put());
have the same issue?The hope is that the explicit call to
put()
is a clue that “Hey, I’m arrowing through an object and mutating it at the same time. That doesn’t seem right.” On the other hand, people don’t think of the&
operator as a mutating operator.I think the article you’re looking for is this one.