22

There are 4 operators in C++ which can be overloaded but cannot be overloaded as freestanding (aka nonmember, standalone) functions. These operators are:

  • operator =
  • operator ()
  • operator ->
  • operator []

This thread explains perfectly well the rationale behind prohibiting operator = to be a nonmember function. Any ideas about the other three?

Community
  • 1
  • 1
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 3
    I'm pretty sure `->*` can be implemented as a nonmember function. – James McNellis Oct 14 '10 at 22:18
  • 2
    @James: You're correct. @Armen: Probably just short-sightedness. – GManNickG Oct 14 '10 at 22:20
  • @James. The book C++ in a nutshell says otherwise... – Armen Tsirunyan Oct 14 '10 at 22:21
  • 1
    @Armen Tsirunyan you should check standard – Andrey Oct 14 '10 at 22:27
  • i guess you can't overload [] and () so that you override real array access and function call. – Andrey Oct 14 '10 at 22:31
  • 2
    @Andrey: The same can pretty much be said about ALL other operators, so I don't think this is the answer. – Armen Tsirunyan Oct 14 '10 at 22:33
  • @Andrey "_i guess you can't overload [] ... so that you override real array access and function call._" The builtin prototype for `[]` is `operator[] (ObjectType*, Integer)`. A user-defined overload can only be declared if it takes a user-defined type by value or reference, so you wouldn't be able to declare an `operator[] (String*, int)` anyway. OTOH, you would have been able to declare `operator[] (int*, Enum)`, for some enumeration type `Enum`. Is that what you have in mind? – curiousguy Dec 12 '11 at 02:09
  • @Andrey "_i guess you can't overload ... () so that you override real ... function call._" Do you fear someone would declare `Date operator() (String(*)(const Date&), const String&)`? Clearly function call **is special**: it is the only builtin operator that normally takes arguments of class type. I guess no one want to overload `operator() (foo(*)(bar),baz)`. The issue is: why isn't it possible to declare `operator() (Fun,baz)` where `Fun` is a user-defined type, or reference to one? – curiousguy Dec 12 '11 at 02:31

2 Answers2

21

The four operators mentioned in the original posting, =, (), -> and [], must indeed be implemented as non-static member functions (by respectively C++98 §13.5.3/1, §13.5.4/1, §13.5.5/1 and §13.5.6/1).

Bjarne Stroustrup's rationale was, as I recall from earlier debates on the subject, to retain some sanity in the language, i.e. having at least some things you could rely on no matter how much Someone Else has screwed up by defining non-member operators for existing classes.

I'm not sure I completely agree that the restriction really helps with that, but.

EDIT: I consulted Bjarne Stroustrup about this (he's always helpful) but it appears that the apparent inconsistencies of the rules are no more than a case of frozen historical accident. He notes that "It looks worse now than it was then because our rules for lvalues and references have changed since the overloading rules were formulated. I tried to look into this issue again a couple of years ago, but ran out of time before producing a complete proposal."

Cheers & hth.,

PS: "The Design and Evolution of C++" book is great for this kind of question, but unfortunately I don't have it.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • And what exactly in this case is the principal difference between -> and ->* ? Why one can't be a nonmember while the other can? I am by the way open to answers like "there is no serious reason. It's illogical, but that's the way it is", though I certainly would be disappointed if it was the case – Armen Tsirunyan Oct 14 '10 at 22:46
  • @Armen: good question. I don't know. Perhaps this is explained "The Design and Evolution", and if so, perhaps someone who has that book will chime in. I checked in the old ARM, it doesn't seem to discuss it. – Cheers and hth. - Alf Oct 14 '10 at 22:57
  • The rationale for `=` is indeed presented in D&E and it is valid to this day. I don't know why this restriction was also imposed on `()`, `[]`, and `->` though. – AnT stands with Russia Aug 15 '11 at 20:59
  • "_having at least some things you could rely on no matter how much Someone Else has screwed up_" Quite funny, in a language where it's possible to overload `&&`, `||`, `,`, but then they loose their usual semantic and the special semantic cannot be re-created. – curiousguy Dec 12 '11 at 02:00
  • @curiousguy: It can indeed be re-created in many expression template systems. – Puppy Mar 09 '12 at 18:32
  • @DeadMG It can be done at the emulation meta level; this last sentence doesn't sound right so I'll give an example: `x && f()` where `&&` is `operator&& (BoolExpr,BoolExpr)` will call `f()` even if `x` is not "true", but will not evaluate the `BoolExpr` result of `f()`, which represents a lazily evaluated `bool`. Also, expression template only work with expressions, not instructions, so you can write `x && y;`, `x || y` but not `if (x) y;`, `if (!x) y;` and not even all expressions: you cannot have `x ? y : z;` either. Which can of sucks. Which is the reason I dislike this "clever" technique. – curiousguy Mar 10 '12 at 03:20
2

This thread on comp.std.c++ discusses the question.

Francis Glassborow, who was on the committee, stated:

The language designers did not want to support conversions and promotions on the left-hand operand of operator =, nor such on the operand of () and [].

Trying to avoid the situation where:

class A {};

class B { B(A& a) {} };

int operator()(B const& b) { return 0; }


int main(void)
{
    A a;

    // This works even though A doesn't have a () operator
    // It creates a temporary B and calls operator()(B& b)
    return a();                   
}
sbi
  • 219,715
  • 46
  • 258
  • 445
Dingo
  • 3,305
  • 18
  • 14
  • 3
    Nit: you'd need `int operator()(B const& b)` to avoid trying to bind a temporary to a reference to non-`const`. Anyway, Armen's question about `->*` still stands. – Cheers and hth. - Alf Oct 14 '10 at 23:40
  • 2
    "The language designers did not want to support conversions and promotions on the left-hand operand of ()". However they do allow that when a class declares a conversion-function to pointer-to function... – Armen Tsirunyan Oct 14 '10 at 23:50
  • 2
    I don't see the validity of the objection. If `B` has `operator()` and can be implicitly created from an `A`, why block it? – GManNickG Oct 15 '10 at 00:07
  • @GMan: See Alf P. Steinbach's comment, "to retain some sanity". – MSalters Oct 15 '10 at 08:39
  • 1
    @MSalters: I don't see it as insane. :) – GManNickG Oct 15 '10 at 08:46
  • @Armen yes, but in that case the code writer has explicitly stated they want B to implicitly convert to A. In the sample above, the writer and user of A would be surprised at the lack of error message. – Mooing Duck Dec 11 '11 at 17:28
  • @MooingDuck "_the writer and user of A would be surprised at the lack of error message_" Why? – curiousguy Dec 12 '11 at 01:51
  • "_The language designers did not want to support conversions and promotions on the left-hand operand of operator =, nor such on the operand of () and []._" That's kind of saying "Because." Why would the language designers not want "conversions on the left-hand operand of operator =, nor such on the operand of () and []"? How are these operators special? – curiousguy Dec 12 '11 at 01:56
  • @curiousguy: If I look at the header for A, and then call A(), I would expect an error since I see no matching call. However, if A was implicitly converable to B, then I would expect to be able to use B's operators as well. A freestanding can unintentionnally convertconstruct from an A, where a conversion operator shows intent. – Mooing Duck Dec 12 '11 at 06:26
  • @MooingDuck This looks like you are assuming the conclusion. The question is why don't you expect `a()` (with `a` of type `A`) to mean `B(a).operator()()`. Why would `a()` mean `a.operator()()` and not `/* not correct C++ */ operator()(a)`? `a + b` can mean either `a.operator+ (b)` or `operator+ (a, b)`. Same for `+=`, etc. `a + b` can mean `operator+ (B(a), b)`, `a += b` can mean `operator+= (B(a), b)` if the prototype is `operator+= (B, B)` (that is, if `+=` does **not** modify the lhs, which is not unrealistic). How is `a (b)` fundamentally different? – curiousguy Dec 13 '11 at 04:35