Why overloaded operators cannot be defined as static members of a class?

廉价感情. 提交于 2019-12-18 12:45:16

问题


C++ syntax allows defining overloaded operators either inside the struct/class like:

struct X
{
   void operator+(X);
}

or outside of the struct/class like:

void operator+(X, X);

but not as

struct X
{
   static void operator+(X, X);
}

Does any body know reasons for this decision? Why the third form is not allowed? (MSVC gives a syntax error). Maybe there is some story behind this?

p.s. Presence of the first and the second definitions at the same time creates ambiguity:

1>CppTest1.cxx
1>c:\ballerup\misc\cf_html\cpptest1.cxx(39) : error C2593: 'operator +' is ambiguous
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(13): could be 'void B1::operator +(B1 &)'
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(16): or       'void operator +(B1 &,B1 &)'
1>        while trying to match the argument list '(B1, B1)'

I do not understand why this ambiguity is any better than between 1,3 or 2,3.


回答1:


I have no specific knowledge of any C++ discussion of this concept, so feel free to ignore this.

But to me, you've got the question backwards. The question should be, "why would this syntax be allowed?"

It provides no advantages at all over the current syntax. The non-static member function version has the same access to private members as your proposed static version. So if you need to access the privates to implement it, just make it a non-static member, exactly as you generally do with most members of a class.

It doesn't make it easier to implement asymmetric operators (ie: operator+(const X &x, const Y &y)). If you need private access to implement this, you'd still need a friend declaration for them in one of the classes.

So I would say that the reason it doesn't exist is that it isn't necessary. Between non-member functions and non-static members, all of the necessary use cases are covered.


Or, to put it another way:

Free functions can do everything that the static function system can, and more.

Through the use of free functions, you can get argument-dependent lookup happening for operators used in templates. You can't do that with static functions, because those would have to be a member of a particular class. And you cannot add to a class from outside of the class, while you can add to a namespace. So if you need to put an operator in a particular namespace in order to make some ADL code work, you can. You can't do that with static function operators.

Thus, free functions are a superset of everything that your proposed static function system would provide. Since there is no benefit to allowing it, there is no reason to allow it, and therefore it is not allowed.


which would make possible to use functors without instantiating them?

That is a contradiction in terms. A "functor" is a "function object". A type is not an object; therefore, it cannot be a functor. It can be a type that, when instantiated, will result in a functor. But the type alone will not be a functor.

Furthermore, being able to declare Typename::operator() static would not mean that Typename() would do what you want. That syntax already has an actual meaning: instantiate a Typename temporary by calling the default constructor.

Lastly, even if all that weren't the case, what good would that actually be? Most template functions that take a callable of some type work just as well with a function pointer as with a functor. Why would you want to restrict your interface, not merely to just functors, but to functors which cannot have internal data? That means you wouldn't be able to pass capturing lambdas and so forth.

What good is a functor that cannot possibly contain state? Why do you want to force the user into passing "functors" that don't have state? And why do you want to prevent the user from being able to use lambdas?

So your question is derived from a false assumption: even if we had it, it wouldn't give you what you want.




回答2:


Because there isn't an obvious syntax to call such an operator, which would mean we'd have to make up something. Consider the following variables:

X x1;
X x2;

Now, let's pretend for a moment that we're using normal member functions instead of operators - let's say I changed operator+ to plus in your example.

Each of the three calls would look like:

x1.plus(x2);
plus(x1, x2);
X::plus(x1, x2);

Now when making an operator call using + how would the compiler know to look up your operator in the scope of X? It can't do it for normal static member functions, and operators aren't given special dispensation to disambiguate.

Now consider if you had both the second and third forms declared in your program. If you said x1 + x2 the compiler would either have to always pick the free function or the call would be ambiguous. The only real alternative would be something like x1 X::+ x2 which just looks ugly. Given all that, I'm sure the standards committee decided to simply forbid the static member version since anything it could accomplish could be done with a friend free function instead.




回答3:


Static member functions can be used to utilities which help a class but for one reason or another are not members. It's easy to envision that among the utilities expressed as static class member functions, it might be useful to have operators.

Of course, if some overloaded operator takes a class C as its primary argument, there is no good reason to want that to be a static member of class C. It can just be a non-static member, so it obtains that argument implicitly.

However, a static member of class C might be an operator overloaded on some class other than C.

Say that there exists a file scope operator ==(const widget &, const widget &);. In my squiggle class, I am working with widget objects, but want a different comparison for them.

I should be able to make a static squiggle::operator == (const widget &, const widget &); for myself.

From the class scope, this is easy to call:

void squiggle::memb(widget a, widget b)
{
   if (a == b) { ... } // calls static == operator for widgets
}

from outside the class scope we can only call it using the explicit scope resolution combined with explicit operator call syntax:

void nonmemb(widget a, widget b)
{
   a == b;  // calls the widget member function or perhaps nonstatic operator
   squiggle::operator ==(a, b); // calls squiggle class' utility
}

This is not a bad idea. Furthermore, we can do it with regular overloaded functions, just not with operators. If comparison of widgets is done with a compare function, then there can be a non-member compare or a widget::compare and there can be a squiggle::compare that takes widgets.

So the only aspect of this which is not supported in C++ is the syntactic sugaring with operators.

Perhaps it's not a sufficiently useful idea to warrant support (so far!) After all, this isn't something which would allow some revolutionary reorganization of a C++ program. But it would fix an incompleteness in the language.

Also, consider that class overloads of operators new and delete are implicitly static! So the incompleteness already has a little exception.




回答4:


hmmm... I am thinking about a static operator() which would implicitely delete all constructors... That would give us kind of typed functions. Sometimes I wish we had it in C++.




回答5:


This might be the reason.

Because each operator need one or more operands. So if we will declare it as static then we can't call it using objects(operands).

In order to call it upon some operand which is nothing but an object the function has to be non-static.

Below is a condition which must be satisfied while doing function overloading.

  • It must have at least one operand which is of user defined type.

So suppose we declare our operator overloading function as static. Then 1st of all the above condition will not be satisfied.

Another reason is, inside static functions we can access only static data members. But while doing operator overloading we have to access all the data members. So if we will declare our operator overloading function as static we cannot access all the data members.

So operator overloading function has to be a non-static member function.

But there an exception.

If we use a friend function for operator overloading then it can be declared as static.




回答6:


I am not aware about any direct drawbacks that allowing static operator + could cause (maybe thinking long enough will produce some theory). But I think at least the principle "don't pay for what you don't use" declared by Bjarne Stroustrup is already good enough answer. What will you gain if that static operator will be allowed except for more complicated syntax (you'll have to write "X::operator+" everywhere instead of just "+") ?




回答7:


Basically, a class member static operator doesn't buy anything over a non-static member.

Any operator defined for a class has to take at least one argument of that class type.

A member operator takes that argument in the form of the implicit this parameter.

A non-member operator has an explicit argument of that class type.

The operator interface to the operator function doesn't care; when we invoke a + b, it take care of generating the code to pass a either via the this parameter or as an explicitly declared parameter. So we aren't expressing any difference in nuance with static versus non-static as to how the operator is used.

Suppose that suddenly a requirement were introduced that the latest ISO C++ must support static member operators. In a hurry, this requirement could be implemented by a source-to-source rewrite according to the following pattern:

static whatever class::operator *(class &x) { x.fun(); return foo(x); }

-->

whatever class::operator *() { (*this).fun(); return foo(*this); }

-->

whatever class::operator *() { fun(); return foo(*this); }

The compiler rewrites the static member operator to non-static, deletes the leftmost parameter, and (with proper lexical hygiene w.r.t. shadowing) replaces all references to that parameter with the expression *this (the unnecessary uses of which can be elided).

This transformation is simple enough that the programmer can be relied upon to write the code that way in the first place.

The static operator function defining mechanism is less powerful. It cannot be virtual for instance, whereas the non-static one can be.



来源:https://stackoverflow.com/questions/11894124/why-overloaded-operators-cannot-be-defined-as-static-members-of-a-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!