On the overloading of the address-of operator & in smart pointer classes

Raymond Chen

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.

5 comments

Discussion is closed. Login to edit/delete existing comments.

  • Michael EnsMicrosoft employee 0

    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 it. Oops! Didn’t hold a reference long enough, so you risk randomly corrupting your data. You have a decent chance of getting “lucky” if this is a linked list with single-threaded access, though, since there will be another reference from the previous list entry or from the head of the linked list, and therefore the data wouldn’t corrupt. This is the behaviour we saw on the x86 and x64 compiler.

    When we later added ARM compiles, we saw right-to-left evaluation of arguments. So we passed the address of the smart pointer and also released/null’d it. Then, the “this” pointer was unconditionally null. Null pointer write AV guaranteed.

    (I tried to search whether you covered something like this before but you have so much content, and the only thing I found was a reference to how C# does define order of parameter evaluation with a dead link presumably to the C# documentation).

    • Raymond ChenMicrosoft employee 0

      I think the article you’re looking for is this one.

    • Neil Rashbrook 0

      Wouldn’t something like spLinkedList->Next(spLinkedList.put()); have the same issue?

      • Raymond ChenMicrosoft employee 0

        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.

    • Dmitry 0

      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.

Feedback usabilla icon