C ++编译器如何扩展前缀和后缀运算符++()?

时间:2017-05-18 19:24:34

标签: c++ operator-overloading operators prefix postfix-notation

考虑:

class Example
{
private:
    int m_i;

public:
    Example(int i) : m_i{i} {}

    //Post-fix
    Example operator++(int) {m_i++; return *this;}

    //Pre-fix
    Example& operator++() {m_i++; return *this;}

    void print() const {std::cout << m_i << '\n'}
};

我正在试验这个以确定编译器如何扩展对前缀和后缀运算符的调用。

例如,当我写这样的东西时:

Example e = 1;
e++;

我希望它扩展到类似&#34; e.operator ++(int)&#34;,或者更进一步,我期待

e++(2);

扩展为类似&#34; e.operator ++(2)。&#34;,但我得到的是编译器抱怨某些&#34;不匹配调用&#39;(示例)(int)&#39;&#34;。

接下来我很好奇&#34; ++ e&#34;神秘地扩展为&#34; e.operator ++()&#34;,即返回引用的那个。

再玩一些,我最终得到了:

Example e = 1;
++e++;
e.print();

哪个打印2,然后:

Example e = 1;
(++e)++;
e.print();

打印3。

我理解(++ e)返回对象的引用,然后将其递增1,所以这是有道理的。我也怀疑&#34; ++ e ++&#34;在这里给出后缀运算符优先级(正如我在另一篇文章中读到的那样),所以这是递增后缀运算符返回的临时变量。这也是有道理的。这让我想知道像

这样的表达方式
++++e
++e++++
++++e++
++++e++++

被扩展(它们都编译并运行预期结果)。

真的,内部到底发生了什么,编译器如何知道调用哪个运算符++(),以及如何扩展这些表达式(特别是在前缀的情况下)?占位符变量在&#34; operator ++(int)&#34;?

中的用途是什么

1 个答案:

答案 0 :(得分:2)

  

“operator ++(int)”中占位符变量的用途是什么?

因为++运算符有两个不同的函数:postfix - ++和prefix - ++。因此,当重载它时,必须有两个不同的功能签名。

  

编译器如何知道调用哪个运算符++(),

当您的代码使用前缀 - ++(例如:++e;)时,将调用带有签名operator++()的函数。当您的代码使用postfix - ++(例如:e++;)时,将调用带有签名operator++(int)的函数,编译器将提供未指定的伪参数值。

从技术上讲,operator++(int)的实现可以使用伪参数值。您可以通过编写e.operator++(5);而不是e++;来传递自己的价值。但这会被认为是错误的编码风格 - 当重载运算符时,建议保留内置运算符的语义,以避免混淆读取代码的人。

请注意,您当前的postfix实现 - ++不遵守此规则:正常的语义是应该返回前一个值;但是你的代码会返回更新后的值。

  

++e++++;

要解析此语句,您需要了解这些解析规则:

  • 标记由“maximal munch”解析,即这意味着++ e ++ ++;(而不是一些一元 - +运算符)。
  • 语言语法根据这些标记确定哪些表达式是哪些运算符的操作数。此过程可以在precedence table
  • 中汇总

咨询该表达式的表格会告诉您:++(((e++)++))。使用前面提到的扩展,可以用函数调用表示法编写:

((e.operator++(0)).operator++(0)).operator++();

在这种情况下,需要从左到右调用这些函数,因为在调用它的表达式之前无法输入成员函数。

因此,假设我们在此语句之前有Example e(1);,则按以下顺序发生以下函数调用:

  • e.operator++(int) - 将e.m_i设置为2并返回一个临时(我将其称为temp1作为伪代码)temp1.m_i2 }。
  • temp1.operator++(int) - 将temp1.m_i设置为3,并返回temp2m.i
  • 3
  • temp2.operator++() - 将temp2.m_i设置为4并返回对temp2的引用。

NB。我的回答只谈到重载运算符是一个成员函数。也可以将++(两种形式)作为非成员重载。在这种情况下,行为将不会改变我的描述,但“写入函数调用符号”表达式将采用不同的语法。