Why can overloaded operators not be defined as static members of a class?
Asked Answered
L

9

53

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 anybody know reasons for this decision? Why is the third form not allowed? (MSVC gives a syntax error.) Maybe there is some story behind this?

P.S. The presence of the first and 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.

Loram answered 10/8, 2012 at 1:4 Comment(12)
What could you do with the static operator overload that you couldn't do with a friend or nonmember?Wyrick
+1... the reasons behind this design decision might prove interesting (if there are any)Ninety
void operator+(X);? Don't you mean X& operator+(X)? I might be the only one confused here, so please correct me if I'm wrong.Callicrates
@E_net4: In the field, yeah you'd do that, but not strictly necessary in the language.Blankenship
@E_net4, this is not the subject of the OP.Loram
@KirillKobelev aren't you the OP?Ninety
@Luchian Grigore, not fully clear...Loram
@Blankenship operator+ should return X, not X&.Panpipe
It could be useful for making classes look like functions/arrays for prototyping/simulation. It would also alleviate the problem of not being able to define members of class with illegal characters via arrays, like WindowingSystem["visibleByDefault:"](true) for smalltalk-style setters that use ":" in their name.Misha
@JamesMcNellis I have a case for static operator overload. Suppose I write some utility class which is a class template and has type parameters. When I instantiate specific class with given types I want to provide common operator for provided template types which doesn't care about exact type implementation. For me it would be convinient to define || operator which acts as in JavaScript and returns one of given operands determined by their validity.Grantland
To expand a tiny bit on the @MarkB answer: Seems to me that the second form that can't be de-sugared. Expanding the syntax sugar to what it would have to mean to call that form: x1 = X::operator+(x2, x3); There is no way for the parser to deduce that from x1 = x2 + x3; Whereas the first x1 = x2.operator+(x3); and third x1 = ::operator+(x2, x3); forms can be deduced.Spiroid
I came across this with operator[], which clearly does not require an instance (if it refers to a static container). Using friend gives the same error (operator[] must be a member function). Outside the class I would need to use a template with instantiations and lose the private-ness of the (static) map(s) I am using. So, the standard appears needlessly dogmatic and could easily dig a bit deeper for an actual problem.Pennyroyal
P
17

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.

Pyrimidine answered 10/8, 2012 at 1:28 Comment(18)
"Free functions can do everything that the static function system can, and more." But that's an argument not to have static functions at all.Parcheesi
The only reason for static class functions is for the class::function() syntax. I suppose if you really wanted to type class::operator+(A, B), then requesting static makes sense but....Mackenie
@Kaz: My understanding is that static member functions predate namespaces; and were added to the language to achieve similar name collision resistance. I could be wrong there.Lafrance
@NicolBolas "What good is a functor that cannot possibly contain state?" It is a more efficient alternative to templating against a simple C function: https://mcmap.net/q/353584/-when-are-stateless-class-functors-useful-in-place-of-a-c-style-function I do have templated functions that do not require an instance of the functor they are templated against, but that instantiate themself the functor, and I do not believe is necessarily bad programming practice.Translucent
@Antonio: OK, let me restate that: why do you want to force the user to use a non-state-containing function as a callback? You can't store types without creating an instance of some type. You can only store objects, so if you want to store this functor for later, you'll have to instantiate it even if we do have static operator(). And if you're not storing it... then leave the decision up to the user as to whether or not they want to have meaningful state. If there's no meaningful state, then it's just an empty class, like a lambda. Speaking of which, they can't use lambdas with this.Pyrimidine
@Antonio: So what do you have to gain by forcing the user to make a non-state-containing static operator()?Pyrimidine
@NicolBolas 1) I do not need to store the functor for later. 2) There are cases in which the purpose of the functor is really small and the benefit for the user is that he has one argument less for his function and one line of code less to instantiate the class 3) I cannot use C+11 at the moment, so I haven't studied lambdas yet :) 4) But in the end I understand that constraining so much about the functor class, I could also constrain to pass a class containing a static method with some name... It's a too small extra burden compared to the effort that people writing the compiler would have...Translucent
...to do to distinguish a constructor from an operator() ... Because if I have to call explicitly the ::operator(), than there's no gain at all in code elegance.Translucent
@Antonio: "he has one argument less for his function" But he doesn't; he has to pass the typename to the template function. It's a different kind of argument, but he has to do funcName<FuncType>(...) rather than funcName(..., FuncType()). Unless you have some means to detect FuncType without the user explicitly providing it.Pyrimidine
@NicolBolas True, but 1) I believe "passing" a functor class in this way it a much clearer way for the user to understand "ehi, this function/method will be actually doing something different because of my choice", with a choice that he knows will be solved at compile time 2) It allows me to do cool stuff like this: https://mcmap.net/q/353585/-may-i-use-a-constant-number-to-choose-a-class-at-compile-time-possibly-using-templates (user passes an enum to select at compile time which sub-functions the function will use)Translucent
let us continue this discussion in chatPyrimidine
To assign the bounty (for what it is worth) I would like your second part of your answer to contain some elements of the discussion we had. In any case, thank you!Translucent
@Parcheesi i'm with kaz, who needs "private", "protected", and "static", when you can just have nonfriend nonmembers and queryinterface to expose alternative views on the class with additional members. Let's get rid of the class keyword altogether, struct is enough, let's save some keywords!Misha
A possible answer to "why should this be allowed" is because it takes extra work in the C++ compiler to disallow the combination. It's like asking, why should a for nested in another for be allowed. Because we have a compiling strategy in place which handles nested statements just fine, so it would be a weird exception to the generality to diagnose and reject that specific case, requiring extra code.Parcheesi
@Kaz: "It's like asking, why should a for nested in another for be allowed." No, it's not. The semantics of nested for statements are obvious, and the lack of such a feature would just get people to do pointless things like call a function that does the inner for. The lack of ability to have a static operator overload doesn't inhibit the programmer in any way. If you can add a static member, you can add a non-static one. So there is no actual need for static operators, and "because we could" alone is not good enough of a reason to do something.Pyrimidine
If you are looking for (reasonable) example, I made logger that wraps printf like logging, so basically my Logger creates Printer instance which in turn forwards operator << to member stringstream. It is printed out in destructor. Unfortunately I can't simply use Logger << something because I need logger object even though it is here just to create temporary printer. The only logic in Logger is actually compile time (for example provides dummy Printer which does nothing if we decide to remove lowest level logs, compiler will optimize it out leaving only calls that have side effects).Sassaby
A couple of years late, but here's a motivating example: traits classes. suppose you want to design a simd library and provide overloads of operator& for a set of a abi types using a traits class. traits<abi>::operator&(...). You can work around this with named functions as: traits<abi>::and(...), but you might recognize that "and" is a keyword in C++. So you have to mangle the name: 'traits<abi>::op_and(...). At this point, it would be much more clear if you could just provide the traits overload with the same operator name.Spermatozoid
"It provides no advantages at all over the current syntax." - Not true, it would solve my problem here: #70230825 (since member operator functions don't have this problem)Caxton
M
20

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.

Melmon answered 10/8, 2012 at 2:36 Comment(5)
"if you had both the second and third forms". And what if I have both the first and the second? I do not feel the difference...Loram
"x1+x2" and there is only the first form of overloading. How can compiler find it? If it can, why can't it find other member of the same class?Loram
This rationale is false. If overloaded operators existed as static functions, the would be in a class scope. From that class scope, they would be called as usual. From outside of the scope, you could use scope resolutions syntax along with explicit operator call: foo_class::operator +(a, b).Parcheesi
This would be the definitive answer if it didn't constantly talk about "the compiler", who really has nothing to do with any of this.Saxen
My final conclusion is that operator overloading are there for syntax sugar, and it would be possible to call static operators only using the explicit call, which is exactly against the idea of syntax sugaring, so this was the reason that lead to the decision of not allowing static operator overload. I choose your answer as the one that best stresses this idea, even if I think operator() would have deserved a separate section.Translucent
P
17

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.

Pyrimidine answered 10/8, 2012 at 1:28 Comment(18)
"Free functions can do everything that the static function system can, and more." But that's an argument not to have static functions at all.Parcheesi
The only reason for static class functions is for the class::function() syntax. I suppose if you really wanted to type class::operator+(A, B), then requesting static makes sense but....Mackenie
@Kaz: My understanding is that static member functions predate namespaces; and were added to the language to achieve similar name collision resistance. I could be wrong there.Lafrance
@NicolBolas "What good is a functor that cannot possibly contain state?" It is a more efficient alternative to templating against a simple C function: https://mcmap.net/q/353584/-when-are-stateless-class-functors-useful-in-place-of-a-c-style-function I do have templated functions that do not require an instance of the functor they are templated against, but that instantiate themself the functor, and I do not believe is necessarily bad programming practice.Translucent
@Antonio: OK, let me restate that: why do you want to force the user to use a non-state-containing function as a callback? You can't store types without creating an instance of some type. You can only store objects, so if you want to store this functor for later, you'll have to instantiate it even if we do have static operator(). And if you're not storing it... then leave the decision up to the user as to whether or not they want to have meaningful state. If there's no meaningful state, then it's just an empty class, like a lambda. Speaking of which, they can't use lambdas with this.Pyrimidine
@Antonio: So what do you have to gain by forcing the user to make a non-state-containing static operator()?Pyrimidine
@NicolBolas 1) I do not need to store the functor for later. 2) There are cases in which the purpose of the functor is really small and the benefit for the user is that he has one argument less for his function and one line of code less to instantiate the class 3) I cannot use C+11 at the moment, so I haven't studied lambdas yet :) 4) But in the end I understand that constraining so much about the functor class, I could also constrain to pass a class containing a static method with some name... It's a too small extra burden compared to the effort that people writing the compiler would have...Translucent
...to do to distinguish a constructor from an operator() ... Because if I have to call explicitly the ::operator(), than there's no gain at all in code elegance.Translucent
@Antonio: "he has one argument less for his function" But he doesn't; he has to pass the typename to the template function. It's a different kind of argument, but he has to do funcName<FuncType>(...) rather than funcName(..., FuncType()). Unless you have some means to detect FuncType without the user explicitly providing it.Pyrimidine
@NicolBolas True, but 1) I believe "passing" a functor class in this way it a much clearer way for the user to understand "ehi, this function/method will be actually doing something different because of my choice", with a choice that he knows will be solved at compile time 2) It allows me to do cool stuff like this: https://mcmap.net/q/353585/-may-i-use-a-constant-number-to-choose-a-class-at-compile-time-possibly-using-templates (user passes an enum to select at compile time which sub-functions the function will use)Translucent
let us continue this discussion in chatPyrimidine
To assign the bounty (for what it is worth) I would like your second part of your answer to contain some elements of the discussion we had. In any case, thank you!Translucent
@Parcheesi i'm with kaz, who needs "private", "protected", and "static", when you can just have nonfriend nonmembers and queryinterface to expose alternative views on the class with additional members. Let's get rid of the class keyword altogether, struct is enough, let's save some keywords!Misha
A possible answer to "why should this be allowed" is because it takes extra work in the C++ compiler to disallow the combination. It's like asking, why should a for nested in another for be allowed. Because we have a compiling strategy in place which handles nested statements just fine, so it would be a weird exception to the generality to diagnose and reject that specific case, requiring extra code.Parcheesi
@Kaz: "It's like asking, why should a for nested in another for be allowed." No, it's not. The semantics of nested for statements are obvious, and the lack of such a feature would just get people to do pointless things like call a function that does the inner for. The lack of ability to have a static operator overload doesn't inhibit the programmer in any way. If you can add a static member, you can add a non-static one. So there is no actual need for static operators, and "because we could" alone is not good enough of a reason to do something.Pyrimidine
If you are looking for (reasonable) example, I made logger that wraps printf like logging, so basically my Logger creates Printer instance which in turn forwards operator << to member stringstream. It is printed out in destructor. Unfortunately I can't simply use Logger << something because I need logger object even though it is here just to create temporary printer. The only logic in Logger is actually compile time (for example provides dummy Printer which does nothing if we decide to remove lowest level logs, compiler will optimize it out leaving only calls that have side effects).Sassaby
A couple of years late, but here's a motivating example: traits classes. suppose you want to design a simd library and provide overloads of operator& for a set of a abi types using a traits class. traits<abi>::operator&(...). You can work around this with named functions as: traits<abi>::and(...), but you might recognize that "and" is a keyword in C++. So you have to mangle the name: 'traits<abi>::op_and(...). At this point, it would be much more clear if you could just provide the traits overload with the same operator name.Spermatozoid
"It provides no advantages at all over the current syntax." - Not true, it would solve my problem here: #70230825 (since member operator functions don't have this problem)Caxton
P
4

Static member functions can be used to develop utilities which help the implementation of 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.

Parcheesi answered 12/4, 2013 at 22:31 Comment(0)
E
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++.

Exact answered 4/3, 2016 at 14:52 Comment(1)
open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1169r2.html :) . I wish I proposed this, but I guess Barry did it better anyway.Exact
T
1

First, I don't know exactly why static operators aren't allowed in C++. As a Python programmer I've seen some great examples of API flexibility using @classmethod methods, which are called as Class.method and it appears no one has suffered from the existence of such a thing.

My guess would be that in C++ it is probably a language-design-related stuff, since at least I don't see anything else preventing that from happening.

Well now, even though you cannot do it legally, you can cheat using #define's and some luck) DISCLAIMER! : Maybe you should NOT do this at home, but it's up to you


#include <iostream>

// for demonstration purposes
// no actual array implementation
class Array
{
public:

  Array() { std::cout << "Array() created\n"; }

  Array operator()()
  {
    std::cout << "surprising operator() call\n";
    return Array();
  }

  int operator[] (int elem_count)
  {
    return elem_count;
  }

};

#define Array Array()

int main()
{
  // this is not a static method, right, but it looks like one. 
  // and if you need the static-method syntax that bad, you can make it. 
  auto arr = Array[7]; // implicitly acts as Array()[N]
  auto x = Array(); // delegate all construction calls to Array.operator()
  std::cout << arr;
}


So, I think you may overload some other operators that way and make it look syntactically as if they were static.

Testament answered 6/5, 2020 at 12:11 Comment(0)
S
1

I am a couple of years late, but I wanted to offer a contrasting view on this topic from the other answers given. Consider the case of designing a traits class for various ABIs supported by an implementation. For instance, consider the case of designing a traits class for simd ABIs:

template<typename T, typename Abi>
struct abi_traits {};

template<>
struct abi_traits<float, sse>
{
    using vector_type = __m128;

    [[nodiscard]] static constexpr auto operator&(auto a, auto b) noexcept
    {
        return _mm_and_ps(a, b);
    }
};

You'd then be able to call operator overloads for each abi:

abi_traits<T, Abi>::operator&(a, b);

This may seem contrived, but consider the alternative where a named function is used as opposed to an operator overload:

abi_traits<T, Abi>::and(a, b);

You might recognize that "and" is a keyword in C++, and this therefore does not compile. You'd have to mangle the name in some way/shape/form to make this work:

abi_traits<T, Abi>::op_and(a, b);

So then from your vector class, you would have to provide the implementation like this:

template<typename T, typename Abi>
struct simd
{
    ...
    constexpr auto operator&(auto b) noexcept
    {
        traits<T, Abi>::op_and(this->value, b);
    }
};

And you certainly could do this, but then you have to maintain and understand the mangled-name syntax imposed by the programmer. It is much more clear in my mind to allow static overloads, as this would better show intent in the code:

template<typename T, typename Abi>
struct simd
{
    ...
    constexpr auto operator&(auto b) noexcept
    {
        traits<T, Abi>::operator&(this->value, b);
    }
};

So... I get why the other answers might be wary of this... but I totally think there is merit to having static operator overloads where possible. I have seen some proposals appear in the past regarding various efforts to add static operator overloads (for instance, this proposal for static operator()). Perhaps this merits a larger discussion or even a proprosal. I certainly have run into many situations where I thought to myself that it would be much clearer to have this feature. Naming is hard, and it's an underrated problem. If I saw a function with the form op_shl(auto a, auto b), I would be less certain what to do with this function than if I saw a function of the form: operator<<(auto a, auto b). And we can make all the arguments we want about writing good comments and following best-practices with software designs, but at the end of the day, there is no ambiguity with the static operator syntax.

Spermatozoid answered 4/10, 2021 at 13:55 Comment(2)
I'm really surprised by all the bad answers here. Most are based on false premises ("because every operator needs an operand") or they simply claim "It's forbidden because it's useless." This latter point may be correct as the committee's reasoning, but it's a terrible reason to have such an arbitrary rule. In my case I wanted a Singleton class to have an operator -> which would ensure the single instance was instantiated during the static initialization chaos.Goebel
I had another use case here #2614814 (That is in a discussion about namespaces vs structs.)Retarded
P
0

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.

Philipphilipa answered 10/8, 2012 at 3:6 Comment(5)
Static method can have operands that are objects. Where is the problem?Loram
Suoppese you have Obj x3 = x1 + x2; then if, you dont have any object, how to call function for +?Philipphilipa
It is true that static member function cannot access instance members of the class directly. But it can access them once it has an instance. You can write static void operator+(X &x1, X &x2) { x1.NonStaticMethod1(x2.NonStaticDataField); }. The x1 and x2 are instances. This means that static method can access their instance members, including protected/private ones. Implementation of the operator does not need anything other than that.Loram
In ISO C++, operator overloads cannot be static class member functions. Maybe your compiler allows it as an extension. GNU g++ 4.6.1: test.cc:5:48: error: ‘static bool foo::operator==(bar&, bar&)’ must be either a non-static member function or a non-member function. It doesn't matter if we make the first argument foo &.Parcheesi
If we add the friend keyword to static, it is no longer a static member function! It is a nonmember function, and the static keyword takes on the usual meaning closely related to the one in C.Parcheesi
B
0

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 "+") ?

Bumper answered 31/7, 2013 at 13:26 Comment(10)
What about the operator parenthesis, to be used from a functor?Translucent
Why should I write X::operator+??? It should deduce the right operator overload as it is doing this in other cases...Loram
@Antonio: What about it? I covered that one specifically at the bottom of my answer.Pyrimidine
@Kirill As for any static member function of a class, to call it you must specify class name with ::. If instead of defining forbidden static operator+ you wrote allowed static method Add(X&, X&), how would you call it? You would need to write X::Add(x, y). If you had a static member variable let say double myHeight. How will you access it? You must write X::myHeight. The same will apply to operator if it will ever become possible. To refer to static member of a class you must write class name followed by ::. This is C++ syntaxBumper
btw kaz already mentioned this (in more C++ terminology!) in his/her comment Apr 12 at 22:09Bumper
@mvidelgauz, assume class A a1, a2;, then once compiler sees a1 +a2, it can somehow find an instance member in the class A. Why would it be a problem to find a static member? Come on.Loram
it shouldn't be a problem technically of course. But the language has it's design and concepts, it doesn't want to implement everything what is just technically possible. Why if you miss ";" you get compilation errors? It technically possible in many cases to recognize next statement and implicitly assume ";" in its place. For example inside switch scope before next "case" keyword. It doesn't want to break concepts because it may break future language development, it may complicate parsing/compiling algorithms, it may worsen readability; break static code analyzers, highlighters an so on...Bumper
Now, thinking philosophically/conceptually about "regular" built-in operator + (applied to POD), what do you think is it? Member of a type, free member or static member of a type? I know my question may sound stupid at first because POD are not classes and I am talking about their "members", static or non-static. But I am asking CONCEPTUALLY. Every time compiler sees operator + it has to recognize its operands "typename" first, be it POD or class. And we want some polymorphic approaches/logic. For example, when working with templates. From this point of view my question is not THAT stupid.Bumper
I tend to think that in case of POD operator + is free function.Bumper
Following your logic it must be possible to write just Add(x, y) when "Add" is static member of X and both x and y are objects of type X - compiler must be able to "somehow find an instance member in the class". It is also technically possible - just like in case of your static operator +. But language design forbids it. Because according to design any static member must be refereed using class name with ::.Bumper
P
0

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.

Parcheesi answered 22/3, 2018 at 0:11 Comment(2)
You have made the assumption that both operands are of the same type. What if I need to to define std::ostream& operator<<(std::ostream&, whatever const&)?Retarded
@Retarded Yes, exactly; and I noted this in my ... other answer to this question from 2013. Very strange; why did I write this one five years later. I was just looking at this answer about 24 hours ago, wondering whether to delete it.Parcheesi

© 2022 - 2024 — McMap. All rights reserved.