Here’s a summary of the tables we spent the past few days building. Points of interest are highlighted.
 | _com_ptr_t |
MFC IPTR |
ATL CComPtr |
WRL ComPtr |
wil::com_ptr |
C++/WinRTwinrt::com_ptr |
---|---|---|---|---|---|---|
Default construction | Pass | Pass | Pass | Pass | Pass | Pass |
Construct from raw pointer | Pass | Pass | Pass | Pass | Pass | N/A |
Copy construction | Pass | Pass | Pass | Pass | Pass | Pass |
Destruction | Pass | Pass | Pass | Pass | Pass | Pass |
Attach and detach | Pass | Pass | Pass | Pass | Pass | Pass |
Assign to same-type raw pointer | Pass | Pass | Pass | Pass | Pass | Pass (copy_from ) |
Assign to same-type smart pointer | Pass | Pass | Pass | Pass | Pass | Pass |
Fetch the wrapped pointer | GetInterfacePtr() |
GetInterfacePtr() |
p , orimplicit conversion |
Get() |
get() |
get() |
Access the wrapped object | -> |
-> |
->
(suboptimal)
|
-> |
-> |
-> |
Receive pointer via & |
release old | release old | must be empty | release old | release old | N/A |
Release and receive pointer | & |
& |
& |
ReleaseAndGetAddressOf() |
put() |
put() |
Preserve and receive pointer | N/A | N/A | &p |
GetAddressOf() |
addressof() |
N/A |
Return to empty state | Pass | Pass | Pass | Pass | Pass | Pass |
Comparison | Fail | N/A | Pass | Pass | Pass | Pass |
Accidental bypass | Fail | Fail | Pass | Fail | Fail | Fail |
Construct from other-type raw pointer | Pass | Pass | Pass | Pass | Pass | Pass |
Construct from other-type smart pointer | Pass | Pass | Pass | Pass | Pass | Pass |
Assign from other-type raw pointer | Pass | Pass | Pass | Pass | Pass | Pass |
Assign from other-type smart pointer | Pass | Pass | Pass | Pass | Pass | Pass |
T can be final |
Yes | Yes | No | Yes | Yes | Yes |
T::Release must return ULONG |
No | No | Yes | Yes | Yes | No |
T::Release must be __stdcall |
No | No | Yes | No | No | No |
For the most part, all of the smart pointer wrappers can wrap pointers to non-COM objects, provided they have AddRef
and Release
methods which increment and decrement the object reference count.
ATL is the only one that protects against accidental bypass by using the “coloring” technique. However, the cost is that the wrapped object may not be final
. Furthermore, its implementation of “coloring” is suboptimal if the underlying object does not derive from IUnknown
. (If it does, then the “coloring” has no overhead.)
The semantics of the &
address-of operator vary greatly, a topic I had discussed some time ago. ATL is the only one whose &
operator does not release the pointer before producing the address. C++/WinRT does not overload the &
operator at all.
_com_ptr_t
, IPTR
, and C++/WinRT do not provide a way to access the wrapped pointer’s address without freeing it.
Comparison operators break only in _com_ptr_t
. (Comparison operators never worked with IPTR
, so there was nothing to break.)
C++/WinRT does not support constructing directly from a raw pointer. This is annoying because the case we are most likely to use it for a non-IUnknown-derived class is specifically to construct directly from this
:
// Sadly doesn't work RegisterCallback([strongThis = winrt::com_ptr(this)] { ... });
ATL, WRL, and wil require that the Release
method return the new reference count as a ULONG
. ATL even requires that the Release
method be __stdcall
.
The MFC IPTR
macro is not really used any more, but I listed it in the table for completeness. If you try to use it in a modern compiler, you will get warnings about its use of exception specifiers, which were deprecated in C++17.
Glancing through the table, it seems that the least weird wrapper classes for wrapping types that aren’t COM interfaces are WRL::
and wil::
. You do have to make sure that your Release()
method returns the new reference count, but this is likely something you already have readily available, so returning it is not that big of an obstacle.
“ATL is the only one whose & operator does not release the pointer before producing the address.”
Note: In debug mode the operator& asserts if the smart pointer is still holding a reference. The source has a comment:
//The assert on operator& usually indicates a bug. If this is really
//what is needed, however, take the address of the p member explicitly.
Pity that C++/CX types for COM and Windows Runtime were left out of this overview, the only usable approach to be productive in COM while using C++.
You can’t use C++/CX or Windows Runtime smart pointers for things that aren’t Windows Runtime types, so the table would be very simple: “Not possible (duck typing not supported).”