所有的C++运算符都会返回值吗?

85

我使用过的所有C++运算符都会返回某些东西,例如+运算符返回加法的结果。

所有C++运算符都会返回某些东西,还是有一些C++运算符不返回任何东西?


7
这取决于你如何狭义地定义“运营商”这个词。 - molbdnilo
12
这不是标准强制要求的——例如,您可以实现+=返回void,但这并不推荐。此外,函数调用运算符也可以返回void,这是合法的。 - Mircea Ispas
2
问题的上下文是仅限于C++提供的类型,还是也包括用户定义的类型? - Eljay
@user8240761 这仍然很广泛。我假设您指的是基本类型(bool,char,int等),但您是否还包括在标准库中定义的类型?(即std :: vector,std :: list等)。 - Martin York
@Martin York 不,我只是在询问基本类型。 - user8240761
显示剩余2条评论
12个回答

119

不是所有的运算符都会返回结果。

虽然它们可能不是您想象中的运算符,但请注意,deletedelete[] C++“关键字”实际上是运算符;并且它们被定义为具有void返回类型-这意味着它们计算为无效(这不是“something”)。

来自cppreference

void operator delete  ( void* ptr ) noexcept;
void operator delete[]( void* ptr ) noexcept;

14
我喜欢你的回答,让我以不同的方式思考这个问题。 delete表示删除单个对象,delete[]表示删除对象数组,throw表示抛出异常,(void)x;强制转换类型为void,逗号操作符的左侧是执行表达式而无返回结果,右侧是返回void的表达式,当使用throw作为其中一个分支时,?:三元运算符会抛出异常,dfri的operator void()(用户定义类型)和眠りネロク的void operator()()(用户定义类型)。 - Eljay
10
delete 运算符会销毁对象,然后调用 operator delete,这一点也让人感到困惑。因此,delete 运算符和 operator delete 是两个不同的东西 :( - Mooing Duck
7
不确定为什么你在谈论删除运算符时引用了删除函数,但无论如何。 - Deduplicator
4
delete *expression*会销毁对象,然后调用operator delete - NathanOliver
删除操作算不算运算符呢?我以为对象是对传递的对象进行操作的东西?删除操作不需要传递或创建任何对象就可以工作……就像printf一样。 - Yunfei Chen
@YunfeiChen 当然,deletedelete[]运算符需要有东西可以操作(一个操作数):即由newnew[]运算符分配的内存指针。 - Adrian Mole

83

自定义类型的运算符可以被重载,以执行最奇怪的操作。

例如,+ 运算符会返回加法的结果。

但不一定如此:

#include <iostream>
struct foo {
    int value = 0;
    void operator+(int x) {
        value += x;
    }
};

int main () {
    foo f;
    f + 3;
}

这里的operator+将左侧加到了value成员中,其返回类型为void。这是一个虚构的例子,但通常从自定义运算符中不返回任何东西并不罕见。

我所知道的唯一需要返回值的可以进行重载的运算符是operator->。它必须返回一个原始指针或具有operator->的对象。


5
operator->有些特殊,需要返回指针或者具有operator->的对象,不确定是否还有其他例外。 - 463035818_is_not_a_number
1
是的,这是唯一的限制。甚至比较运算符也不能使用 bool 类型。看起来真的很奇怪。 - bracco23
9
也许一个更实用的例子是表达式模板(Expression Templates)。例如,你可以创建一个矩阵库,其中operator*(Matrix const& left, Matrix const& right)返回的不是Matrix,而是MatrixMul,因此如果将其输入到operator+(Matrix const& left, MatrixMul const& right)中,该操作可以是融合乘加运算,这比先乘后加更有效率。 - Matthieu M.
@bracco23,坦率地说,我不这么认为,因为问题不在于返回什么,而在于是否返回了某些东西或没有返回。关于operator->的事情是相关的,也许我会加上它。 - 463035818_is_not_a_number
1
@DarrelHoffman 当 <<>> 被重载为 I/O 操作时,它们应该返回流以便你可以级联它们:stream << foo << bar; - Barmar
显示剩余13条评论

35
要挑剔一下的话,运算符不会返回任何东西。它们只是语言中用于创建表达式的词汇元素。现在,表达式有类型并且可能评估为值,我想这就是你所说的运算符“返回东西”的意思。
而且,确实如此。有些C++表达式的类型为void(因此不会评估为任何值)。有些很明显,有些则不是那么明显。一个好的例子是:
throw std::runtime_error()

throw是C++语法中的一个表达式。您可以在其他表达式中使用它,例如条件表达式。

return goodStatus() ? getValue() : throw std::runtime_error();

throw表达式的类型是void。显然,由于它只会导致执行迅速转移到其他地方,该表达式没有值。


21

所有内置的C++运算符都不会返回任何东西。重载的C++运算符之所以能够返回值,是因为运算符表示法是对一个函数调用的语法糖。

相反,所有运算符都求值得到一个具有明确定义的类型的结果。甚至函数调用运算符void operator()(/*params*/)也是一个void类型。

例如,+'a'是一个int类型,其值为在你的平台上编码的'a'字符的值。

如果你的问题是“C++运算符可以有void返回类型吗?”,那么答案肯定是可以的。


14
@idclev463035818:我对术语“return”有疑议。在C ++中,唯一返回东西的是函数。表达式会被“求值”,而非“返回”。 - Bathsheba
7
类类型的运算符是返回某些内容的方法。 - 463035818_is_not_a_number
4
这是一个重要观点。粗糙的术语会导致混淆。+1。 - Pete Becker
3
@supercat——你的评论诋毁了许多辛勤工作的人,包括我。我们编写标准的人从未期望编译器编写者通过轮询其他编译器(无论是现在还是过去)来填补细节。标准的目标是明确定义C++编程语言的语法和语义,而不是完美无缺的结果;最新标准中解决了许多早期版本未涉及的问题,这源于经验,认识到当时根本无法看到的复杂性。 - Pete Becker
3
@Peter-ReinstateMonica,这是一个更加严谨的版本。表达式I++不返回值。如果i的类型是用户自定义类型,该表达式将被实现为operator++,它是一个返回值的函数。你称之为“语法糖”,我认为这是一个有重要影响的区别。 - Pete Becker
显示剩余5条评论

13
您实际上可以定义一个函数调用运算符返回空。例如:
struct Task {
   void operator()() const;
};

17
你可以定义几乎任何运算符返回空值(但会面临由维护该代码的愤怒群众所带来的愤怒)。 - Yksisarvinen
7
@Yksisarvinen 但至少这一个潜在有用。 - Mark Ransom

11

operator void():用户定义的转换函数,将类型T转换为void

您可以定义特殊的operator void()转换函数。编译器甚至会警告您,Tvoid的转换函数永远不会被使用

#include <iostream>

struct Foo {
    operator void() { std::cout << "Foo::operator void()!"; }
    // warning: conversion function converting 'Foo' to 
    //          'void' will never be used
};
    
int main() {
    Foo f;
    (void)f;            // nothing
    f.operator void();  // Foo::operator void()!
}

根据[class.conv.fct]/1的规定:

[...] 转换函数永远不会被用于将一个(可能带有cv限定符的)对象转换为(可能带有cv限定符的)相同对象类型(或其引用),转换为该类型的(可能带有cv限定符的)基类(或其引用),或转换为(可能带有cv限定符的)void.117

(117) 这些转换被视为标准转换,用于重载决议([over.best.ics]、[over.ics.ref]),因此也用于初始化([dcl.init])和显式转换。 转换为void不会调用任何转换函数([expr.static.cast])。即使从未直接调用执行转换,这些转换函数也可以被声明,并且通过调用基类中的虚拟转换函数可能会被访问到。

然而,正如上面所示,您仍然可以使用显式的.operator void()语法来调用它。


5
语言中定义的(内建的)运算符是用来执行不同种类计算的标记:
  • 算术运算符(+,-,*,/)
  • 自增/自减(++,--)
  • 赋值运算符(=,+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
  • 逻辑运算符(!,&&,||)
  • 关系运算符(==,!=,>,<,>=,<=)
  • 条件运算符?
  • 逗号
等等。这个列表可能非常广泛。
在像这个这个的语言参考中,这些并不一定被引用为返回某个东西,只是执行算术或逻辑操作,通过此方式修改变量的比较等。
由于这种操作会产生一些值,可以将其解释为运算符“返回”的值,但它不同于函数返回值。
另一方面,重载的运算符可以定义为具有某种类型的返回值,甚至可以是void类型,因此,不是所有的运算符都在C++中返回某个值。

4

我假设你是在谈论运算符函数,而不是语言中作为语法单元的运算符。

如果你在任何类型上重载运算符,你实际上可以返回任何你想要的东西。

这也很有道理,因为像*或()这样的操作有时候直观地不会返回它们的输入类型。想象一下将一个复数类型乘以一个实数类型,或者一个返回集合中某个元素的运算符。

你也可以重载++和--运算符以不返回任何东西,从而消除标准版本极易出错的副作用和表达式值混合的问题。


2

不。 考虑这两个例子:

int multiply (int a, int b) {
   return a*b;
}

void multiply_void(int a, int b) {
   cout << a*b;
//or cout << multiply(a,b);
}

第一个函数将返回一个整数值,该值可用于另一个函数。 该值被返回并存储在内存中,以便在必要时使用。如果不需要,则对人类不可见,只是在内存中保持。

第二个函数将输出到默认输出设备(通常为控制台)。 乘法运算符返回的值传递到输出设备。 multiply_void函数不返回实际值。


0

C++运算符是否返回值取决于您如何使用它们。内置的C++运算符会返回一些值,除非强制返回void。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接