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

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

考虑:

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

我预计它会扩展到类似“e.operator++(int)”的东西,或者更进一步,我预计

e++(2);

扩展为“e.operator++(2)”之类的东西,但我得到的却是编译器提示一些“不匹配调用‘(Example) (int)’”。

接下来我很好奇“++e”如何神秘地扩展为“e.operator++()”,即返回引用的那个。

再玩一些,我最终得到:

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

打印了 2,然后:

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

打印出 3.

我知道 (++e) 返回对对象的引用,然后将其后递增 1,所以这是有道理的。我还怀疑“++e++”在这里赋予了后缀运算符优先权(正如我在另一篇文章中读到的),因此这增加了后缀运算符返回的临时变量。这也是有道理的。这让我想知道表达式如何像

++++e
++e++++
++++e++
++++e++++

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

那么,实际上,内部到底发生了什么?编译器如何知道要调用哪个 operator++(),以及如何扩展这些表达式(尤其是在前缀的情况下)? “operator++(int)”中占位符变量的作用是什么?

最佳答案

What is the purpose of the placeholder variable in "operator++(int)"?

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

how does the compiler know which operator++() to call,

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

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

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

++e++++;

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

  • token 由“最大咀嚼”解析,即这意味着 ++ e++++;(而不是一些一元-+ 运算符)。
  • 语言语法根据这些标记确定哪些表达式是哪些运算符的操作数。这个过程可以总结为 precedence table .

查询该表达式的表会告诉您:++(((e++)++))。使用我之前提到的扩展,这可以写成函数调用符号:

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

在这种情况下,需要从左到右调用这些函数,因为在对其调用的表达式求值之前不能输入成员函数。

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

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

注意。我的回答只谈到重载运算符是一个成员函数。也可以作为非成员重载 ++(两种形式)。在那种情况下,行为将与我的描述相同,但“用函数调用表示法编写”表达式将采用不同的语法。

关于c++ - C++ 编译器如何扩展前缀和后缀运算符++()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44056262/

相关文章:

c++ - 如何避免这句话在模板 SFINAE 中是错误的?

operator-overloading - 如何正确使用opCall?

c++ - 在 windbg "x/2"结果中强制执行 vftable 条目,需要考虑什么?

c++ - 无法从 QML 访问 C++ QObject 子类方法

Python-向现有日期添加天数

java - 使用运算符打印语句 ? : causes unexpected output

javascript - 仅使用数学运算从 0->1 或 1->0 更改值

java - Java中 "instanceof"的使用

c++ - 对内存中同一地址的写入之间可能存在数据竞争

c++ - 使用Omnet++从同一个源节点生成定期消息