C++ 为什么不能将重载运算符定义为类的静态成员?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11894124/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:41:00  来源:igfitidea点击:

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

c++operator-overloadingstatic-methods

提问by Kirill Kobelev

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

C++ 语法允许在结构/类中定义重载运算符,例如:

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?

有没有机构知道这个决定的原因?为什么不允许使用第三种形式?(MSVC 给出了语法错误)。也许这背后有什么故事?

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

ps 第一个和第二个定义同时存在会造成歧义:

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,3 或 2,3 之间更好。

采纳答案by Nicol Bolas

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

我没有任何关于这个概念的 C++ 讨论的具体知识,所以请随意忽略这一点。

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.

实现非对称运算符(即:)并不容易operator+(const X &x, const Y &y)。如果您需要私有访问来实现这一点,您仍然需要在其中一个类中为它们声明一个朋友。

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 addto 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.

通过使用免费函数,您可以对模板中使用的运算符进行依赖于参数的查找。您不能使用静态函数来做到这一点,因为它们必须是特定类的成员。并且您不能从类外部添加到类,而可以添加到命名空间。因此,如果您需要将运算符放在特定的命名空间中以使某些 ADL 代码工作,则可以。你不能用静态函数运算符做到这一点。

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 reasonto 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 Typenametemporary by calling the default constructor.

此外,能够声明Typename::operator()static 并不意味着Typename()可以做你想做的事。该语法已经具有实际意义:Typename通过调用默认构造函数来实例化一个临时对象。

Lastly, even if all that weren'tthe 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 cannothave internal data? That means you wouldn't be able to pass capturing lambdas and so forth.

最后,即使情况并非如此,那实际上又有什么好处呢?大多数采用某种类型的可调用对象的模板函数与函数指针和函子一样可以正常工作。为什么你要限制你的接口,不仅限于函子,而且是不能有内部数据的函子?这意味着您将无法传递捕获 lambdas 等等。

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?

不可能包含状态的函子有什么用?为什么要强制用户传递没有状态的“函子”?为什么要阻止用户使用 lambdas?

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

所以你的问题来自一个错误的假设:即使我们拥有它,它也不会给你想要的东西。

回答by Mark B

Because there isn't an obvious syntax to callsuch 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 plusin your example.

现在,让我们暂时假设我们使用的是普通成员函数而不是运算符 - 假设我在您的示例中更改operator+plus

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.

现在,当使用运算符调用时+,编译器如何知道在X? 它不能为普通的静态成员函数做到这一点,并且运算符没有被赋予特殊的特权来消除歧义。

Now consider if you had boththe second and third forms declared in your program. If you said x1 + x2the 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::+ x2which 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.

现在考虑,如果你有两个第二,在你的程序中声明第三形式。如果您说x1 + x2编译器要么必须始终选择自由函数,要么调用将不明确。唯一真正的选择是x1 X::+ x2看起来很难看的东西。鉴于所有这些,我确信标准委员会决定简单地禁止静态成员版本,因为它可以完成的任何事情都可以用朋友免费功能来完成。

回答by Kaz

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.

当然,如果某个重载运算符将类 C 作为其主要参数,则没有充分理由希望它成为类 C 的静态成员。它可以只是一个非静态成员,因此它隐式获取该参数。

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

但是,类 C 的静态成员可能是在除 C 之外的某个类上重载的运算符。

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

假设存在一个文件 scope operator ==(const widget &, const widget &);。在我的squiggle课堂上,我正在处理widget对象,但希望对它们进行不同的比较。

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

我应该能够static squiggle::operator == (const widget &, const widget &);为自己做一个。

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 cando it with regular overloaded functions, just not with operators. If comparison of widgets is done with a comparefunction, then there can be a non-member compareor a widget::compareand there can be a squiggle::comparethat takes widgets.

这不是一个坏主意。此外,我们可以使用常规的重载函数做到这一点,而不是使用运算符。如果小部件的比较是用一个compare函数完成的,那么可能有一个非成员compare或 awidget::compare并且可能有一个squiggle::compare需要widgets.

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

因此,C++ 不支持的唯一方面是使用运算符的语法糖化。

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.

也许这不是一个足够有用的想法来保证支持(到目前为止!) 毕竟,这不是允许对 C++ 程序进行一些革命性重组的东西。但它会修复语言中的不完整性。

Also, consider that class overloads of operators newand deleteare implicitly static! So the incompleteness already has a little exception.

另外,请考虑运算符的类重载new并且delete是隐式静态的!所以不完整性已经有一点例外了。

回答by Slava

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++.

嗯...我在考虑一个静态的 operator() 它会隐式删除所有的构造函数...这会给我们一种类型化的函数。有时我希望我们在 C++ 中有它。

回答by Kaz

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 thisparameter.

成员运算符以隐式this参数的形式接受该参数。

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 aeither via the thisparameter 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.

操作员功能的操作员界面无关紧要;当我们调用a + b,它照顾生成代码的传递a通过任一this参数或显式声明的参数。所以我们没有表达静态与非静态关于如何使用运算符的任何细微差别。

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:

假设突然引入了最新的 ISO C++ 必须支持静态成员运算符的要求。 匆忙中,可以根据以下模式通过源到源重写来实现此要求:

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 staticmember 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).

编译器将static成员运算符重写为非静态,删除最左边的参数,并且(使用适当的词法卫生 wrt 阴影)用表达式替换对该参数的所有引用*this(可以省略不必要的使用)。

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

这种转换非常简单,可以依赖程序员首先以这种方式编写代码。

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

static操作功能定义机制是那么强大。virtual例如,它不能是,而非静态的可以是。

回答by Alex Vask

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.

首先,我不知道为什么 C++ 中不允许使用静态运算符。作为一名 Python 程序员,我已经看到了一些使用 @classmethod 方法的 API 灵活性的很好的例子,这些方法被称为 Class.method 并且似乎没有人受过这样的事情的影响。

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.

我的猜测是,在 C++ 中,它可能是与语言设计相关的东西,因为至少我没有看到任何其他东西阻止这种情况发生。

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

好吧,即使您不能合法地这样做,您也可以使用#define's 和一些运气来作弊) 免责声明!: 也许你不应该在家里这样做,但这取决于你


#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.

因此,我认为您可能会以这种方式重载其他一些运算符,并使其在语法上看起来好像它们是静态的。

回答by mvidelgauz

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

我不知道允许静态运算符 + 可能导致的任何直接缺点(也许考虑得足够长会产生一些理论)。但我认为至少 Bjarne Stroustrup 宣称的“不为你不使用的东西付费”的原则已经是足够好的答案。如果除了更复杂的语法(你必须到处写“X::operator+”而不仅仅是“+”)之外,允许使用静态运算符,你会得到什么?

回答by Narendra

This might be the reason.

这可能是原因。

Because each operatorneed one or more operands. So if we will declare it as staticthen we can't call it using objects(operands).

因为每个都operator需要一个或多个operands。所以如果我们将它声明为static那么我们就不能使用对象(操作数)来调用它

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.

所以运算符重载函数必须是一个non-static member function.

But there an exception.

但有一个例外。

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

如果我们使用友元函数进行运算符重载,那么它可以声明为静态的。