The Standard says
The operand shall have a pointer type, or a class type having a single conversion function (12.3.2) to a pointer type. If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section.
You can (ab)-use the absence of overload resolution by declaring a const version of the conversion function. On a conforming compiler that's enough to make it not work anymore with delete
:
struct A {
operator int*() { return 0; }
operator int*() const { return 0; }
};
int main() {
A a;
int *p = a; // works
delete a; // doesn't work
}
Results in the following
[js@HOST2 cpp]$ clang++ main1.cpp
main1.cpp:9:3: error: ambiguous conversion of delete expression of type 'A' to a pointer
delete a; // doesn't work
^ ~
main1.cpp:2:3: note: candidate function
operator int*() { return 0; }
^
main1.cpp:3:3: note: candidate function
operator int*() const { return 0; }
^
1 error generated.
On compilers that are less conforming in that regard (EDG/Comeau, GCC) you can make the conversion function a template. delete
does not expect a particular type, so this would work:
template<typename T>
operator T*() { return /* ... */ }
However, this has the downside that your smartpointer is now convertible to any pointer-type. Although the actual conversion is still typechecked, but this won't rule out conversions up-front but rather give a compile time error much later. Sadly, SFINAE does not seem to be possible with conversion functions in C++03 :) A different way is to return a private nested type pointer from the other function
struct A {
operator int*() { return 0; }
private:
struct nested { };
operator nested*() { return 0; }
};
The only problem now is with a conversion to void*
, in which case both conversion functions are equally viable. A work-around suggested by @Luther is to return a function pointer type from the other conversion function, which works with both GCC and Comeau and gets rid of the void*
problem while having no other problems on the usual conversion paths, unlike the template solution
struct A {
operator int*() { return 0; }
private:
typedef void fty();
operator fty*() { return 0; }
};
Notice that these workarounds are only needed for compilers that are not conforming, though.