About
RSS

Bit Focus


C++ 中的左值

Posted at 2010-06-19 13:05:49 | Updated at 2018-09-21 07:01:26

请使用 x86 32位 ArchLinux GCC 4.5.0 环境编译文中 C++ 示例代码, 不过只要是 GCC 或 clang++ 编译结果应该都相仿.

作为一个特别的语言, C++ 中的左值是*语义*概念而非*语法*概念, 这一点甚至也可以认为是沿袭自 C 语言. 在任何其他语言中讨论 “什么是左值” 这个概念时, 会简短使用 “变量可以是左值”, “表达式不能是左值”, “常数不能作为左值” 等等, 诸如 “变量”, “表达式”, “常数” 这些都是语法概念, 所以刚才这些说法都是左值的语法约定. 确实一些语言使用语法约束来规定左值, 比如 Python, 下面的 Python 代码

class A:
    pass

a = A()
a + 1 = 0

编译时会报 “SyntaxError: can’t assign to operator”, 是 *Syntax* 哦. -甚至连- JS -这样乱的语言-也有类似的例子, 比如

var f = function() {};
f() = 0;

在执行的时候会报 "ReferenceError: Invalid left-hand side in assignment" --- Chromium.

在 C 里面表达式, 或者函数的返回值, 或者两者的混合, 只要语法恰当, 返回值类型又是一个可写指针, 那么通过对该指针引用的方式来赋值, 如

int* f() { return NULL; }

*(f() + 1) = 0;

这一特性在 C++ 那放荡不羁的类型系统里得到了极大的加强, 完全不再受制于语法的约束, 编译器只需要先根据运算符优先级搭起语法树, 至于左值判定什么的都留在以后的语义分析再来.

而这一切的开端仅仅是因为 C++ 支持一个指针的替代, 也就是引用, 这样连前缀星号都省略了.

另一方面, C++ 中的运算符重载机制又极大地扩展其左值概念的超凡脱俗, 并且 C++ 程序员似乎从来也未有过 “哦, 这只是个示例程序, 尽量别引入复杂特性” 这样的想法, 即使是经典的 Hello World 程序中, 也毫不吝惜地显摆着运算符重载这种高级特性.

#include <iostream>

int main()
{
    std::cout << "Hello, world\n";
}

从这个例子扩展出一个赋值语句几乎毫不费力, 只需要在函数体第一行加个 = 之后随手发挥就好.

#include <iostream>

int main()
{
    std::cout << "Hello World\n" = std::cerr;
    return 0;
}

当然这份代码会报错但不是因为左值不合理, 而是 --- 赋值操作符被设为私有函数或赋值操作符重载被删除了 (C++11 中).

如果希望的话, 可以进一步参考下面这个操作符重载错乱的例子 (它可能由一位危险的 C++ 实习生在主键盘区 += 坏掉的机器上编写的)

struct type {
    int x;

    type& operator+(type& rhs)
    {
        this.x = rhs.x;
        return *this;
    }

    type& operator=(type& rhs)
    {
        this.x += rhs.x;
        return *this;
    }
};

int main()
{
    type a, b, c;
    a + b = c;
    return 0;
}

所以在 C++ 的世界里, 左值跟赋值运算操作符已经没有半点关系了, 倒是只要类型能够匹配上, 运算符重载就能通过编译, 看起来再离奇的算式都没问题.

不过, 如果非要扯赋值运算符, 它跟其它二元运算符有什么不同?

这差异还确实有, 那就是, 赋值运算符必须是成员非静态的, 也就是说下面的编译无法通过

struct type {};

type const& operator=(type&, type const&);

而其它二元运算符都允许以全局静态形式重载, 比如非常必需的流输入输出重载.

最后的最后, 这个规定合理吗? 这个问题就很难回答了, 目前我个人觉得这个限制是很无理的... 而且, 顺带说下, 下面的代码却是可以通过编译的

struct type {};

type const& operator+=(type&, type const&);
type const& operator&=(type&, type const&);

Post tags:   C++  Left Value  Operator Overload

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site