What Legitimate Reasons Exist to Overload the Unary Operator&

What legitimate reasons exist to overload the unary operator&?

I seem to remember something like a smart pointer class which overrode operator& because it wanted to return the address of the contained pointer rather than the address of the smart pointer object. Can't remember where I saw it or whether it seemed like a good idea at the time.

Aha, remembered: Microsoft's CComPtr.

Edit: To generalize, it might make sense under the following conditions:

  • You have an object which is masquerading as some other object.
  • This object can obtain a pointer to the thing it's masquerading as.

Returning anything other than a legitimate pointer would violate the principle of least astonishment.

Why would anyone want to overload the & (address-of) operator?

If you're dealing with any sort of wrapper objects, you might want or need to transparently forward the access to the wrapper to the contained object. In that case, you can't return a pointer to the wrapper, but need to overload the address-of operator to return a pointer to the contained object.

Overloading unary operator &

Use std::addressof function(or boost::addressof pre C++11). In any case, overloading unary & is highly dubious. Why do it instead of having a named function that returns the address of your data?

R unary operator overload: risks?

I executed "!" = function(a){stop("'NOT' is used")} and executed the replications function, which uses the ! operator, and this worked fine. So it looks like it is safe to override "!".

Still you probably want to use classes, which you can do as follows:

# Create your object and set the class
A = 42
class(A) = c("my_class")

# override ! for my_class
"!.my_class" = function(v){
cat("Do wathever you want here. Argument =",v,"\n")
}

# Test ! on A
!A

C++ Unary - Operator Overload Won't Compile

const-correctness

This has to be

 frag operator+ (const frag &oper1, const frag &oper2);

or else the operands can't be temporaries, such as the return value of operator-

And unary minus should rather be:

frag operator - () const;

since it shouldn't modify the operand.

Why are we allowed to take the address of an incomplete type?

Yes, that's intentional, and the possibility of breakage if operator& is overloaded is known.

Taking the address of incomplete types has been possible since long before C++. In C, there is absolutely no risk of any breakage, because & cannot be overloaded.

C++ chose not to unnecessarily break previously valid programs, and simply specified that if an incomplete type does turn out to have an overloaded & operator, it's unspecified whether the overloaded operator gets used.

Quoting N4140:

5.3.1 Unary operators [expr.unary.op]

If & is applied to an lvalue of incomplete class type and the complete type declares operator&(), it is unspecified whether the operator has the built-in meaning or the operator function is called.

This can be interpreted to apply even to a class currently being declared, and even when a declaration of operator& has already been seen:

extern struct A a;
struct A {
int operator&();
decltype(&a) m; // int, or A *?
};
int main() {
return A().m; // only valid if m is int
}

Here, GCC gives m type A * and rejects the program, but clang gives it type int and accepts it.

Why does std::basic_ios overload the unary logical negation operator?

With old (read: not long after cfront) C++ compilers, the compiler was not guaranteed to implicitly call typecast operators on objects when needed. If iostream didn't have an operator ! declared, then you couldn't expect !cout to work in all cases. C++89 (or whatever the pre-C++98 standard was called) simply left the area undefined.

This is also why operator void*() was overloaded, and not operator int or operator bool. (bool didn't even exist as its own type in the standard at that point.) I remember my professor telling me that if(), under the hood, expected a void* in C++, because that type could act as a "superset" type relative to those expression result types that would be passed to an if statement, but I have not found this spelled out anywhere.

This was around the time of gcc 2, when most folks didn't support templates or exceptions, or if they did, didn't fully support them, so metaprogramming C++ with templates was still a theoretical exercise and you made sure to check that operator new didn't return a null pointer.

This drove me nuts for several years.

An interesting excerpt from Stroustrup's The C++ Programming Language, 3rd ed. (1997), page 276:

The istream and ostream types rely on a conversion function to enable statements such as

while (cin >> x) cout << x;

The input operation cin>>x returns an istream&. That value is implicitly converted to a value indicating the state of cin. The value can then be tested by while. However, it is typically not a good idea to define an implicit conversion from one type to another in such a way that information is lost in the conversion.

There's a lot in C++ that seems to be a victory of cute or clever over consistent. I wouldn't mind one bit if C++ was smart enough to handle the above loop as:

while (!(cin >> x).fail()) cout << x;

because this, while more verbose and more punctuation, is clearer to a beginning programmer.

... Actually, come to think of it, I don't like either of those constructs. Spell it out:

for(;;)
{ cin >> x;
if(!cin)
break;
cout << x;
}

Why do I like this better? Because this version makes it far clearer how to expandthe code to, say, handle two reads at a time instead of one. For example, "The existing code copies a sequence of float values. We want you to change it so it pairs up the float values and writes them out, two per line, because we're now using complex numbers."

But I digress.



Related Topics



Leave a reply



Submit