C++ 为什么是++;我认为是l值,但我++;不是吗?

C++ 为什么是++;我认为是l值,但我++;不是吗?,c++,operators,increment,rvalue,C++,Operators,Increment,Rvalue,为什么++i是l值而i++不是?关于左值 在C(例如Perl)中,++i和i++都不是左值 在C++中,i++不是左值,而是++i是左值 ++i相当于i+=1,这相当于i=i+1 结果是我们仍然在处理同一个对象i 它可以被视为: int i = 0; ++i = 3; // is understood as i = i + 1; // i now equals 1 i = 3; 另一方面,i++可以看作: 首先,我们使用i的值,然后增加对象i (编辑:在第一次尝试后更新)。C# 主

为什么++i是l值而i++不是?

关于左值

  • C
    (例如Perl)中,
    ++i
    i++
    都不是左值

  • C++
    中,
    i++
    不是左值,而是
    ++i
    是左值

    ++i
    相当于
    i+=1
    ,这相当于
    i=i+1

    结果是我们仍然在处理同一个对象
    i

    它可以被视为:

    int i = 0;
    ++i = 3;  
    // is understood as
    i = i + 1;  // i now equals 1
    i = 3;
    
    另一方面,
    i++
    可以看作:
    首先,我们使用
    i
    值,然后增加对象
    i

(编辑:在第一次尝试后更新)。

C#


主要区别在于i++返回增量前的值,而++i返回增量后的值。我通常使用++I,除非我有一个非常有说服力的理由使用I++,也就是说,如果我真的需要预增量值

我认为使用“++i”形式是一个很好的练习。虽然在比较整数或其他POD时,增量前后的差异实际上是无法测量的,但如果对象的复制成本很高,或者频繁增加,则在使用“i++”时必须创建和返回的额外对象副本可能会对性能产生重大影响。

POD增量前: 预增量的作用应与对象在表达式之前递增一样,并且在该表达式中可用,就像发生了那样。因此,C++标准COMITEE决定它也可以被用作L值。 POD员额增额: 后增量应增加POD对象,并返回一个副本,用于表达式中(参见n2521第5.2.6节)。因为拷贝实际上不是一个变量,所以将其作为l值没有任何意义

物体: 对象上的前置和后置增量只是语言的语法糖,它提供了调用对象上的方法的方法。因此,从技术上讲,对象不受语言标准行为的限制,而只受方法调用施加的限制

这些方法的实现者可以让这些对象的行为反映POD对象的行为(这不是必需的,而是预期的)

对象预增量: 这里的要求(预期行为)是对象是递增的(意味着依赖于对象),方法返回一个可修改的值,该值看起来像递增发生后的原始对象(好像递增发生在该语句之前)

要做到这一点,需要siple,并且只要求方法本身返回对它的引用。引用是一个l值,因此其行为将与预期一致

增量后的对象: 这里的要求(预期行为)是对象递增(与递增前的方式相同),返回的值看起来像旧值,并且是不可变的(因此它的行为不像l值)

不可变:
要执行此操作,应返回一个对象。如果对象在表达式中使用,它将被复制构造成一个临时变量。临时变量是常量,因此它是不可变的,并按预期的方式运行

看起来像旧值:
这只是通过在进行任何修改之前创建原始文件的副本(可能使用复制构造函数)来实现的。副本应为深度副本,否则对原始副本的任何更改都将影响副本,因此状态将随使用对象的表达式而更改

与预增量相同:
最好在预增量方面实现后增量,以便获得相同的行为

class Node // Simple Example
{
     /*
      * Pre-Increment:
      * To make the result non-mutable return an object
      */
     Node operator++(int)
     {
         Node result(*this);   // Make a copy
         operator++();         // Define Post increment in terms of Pre-Increment

         return result;        // return the copy (which looks like the original)
     }

     /*
      * Post-Increment:
      * To make the result an l-value return a reference to this object
      */
     Node& operator++()
     {
         /*
          * Update the state appropriatetly */
         return *this;
     }
};

顺便说一下-避免在同一语句中对同一变量使用多个增量运算符。你会陷入“序列点在哪里”和未定义的操作顺序的混乱中,至少在C中是这样。我认为其中一些在Java和C#中被清除了。

我在尝试编译时遇到了左值错误

i++ = 2;
但当我把它改成

++i = 2;
这是因为前缀运算符(++i)更改i中的值,然后返回i,因此它仍然可以被分配给。后缀运算符(i++)更改i中的值,但返回旧值的临时副本,赋值运算符无法修改该副本


对原始问题的回答

如果您谈论的是在语句中单独使用增量运算符,就像在for循环中一样,这实际上没有什么区别。Preincrement似乎更有效,因为postincrement必须自身递增并返回一个临时值,但编译器将优化此差异

for(int i=0; i<limit; i++)
...


它们是不同的。选择哪个增量运算符作为多运算符语句的一部分取决于预期的行为。简言之,不,你不能只选择一个。你必须了解两者。

其他人已经解决了增量后和增量前的功能差异

就左值而言,
i++
不能赋值,因为它不引用变量。它指的是一个计算值

就分配而言,以下两种方式在同一种方式下都没有意义:

i++   = 5;
i + 0 = 5;
因为pre increment返回对递增变量的引用,而不是临时副本,
++i
是左值


出于性能原因,当您对迭代器对象(如STL中的迭代器对象)进行增量时,出于性能原因,更倾向于使用预增量成为一个特别好的主意。迭代器对象可能比int更重。

似乎很多人都在解释
++i
是左值的原因,而不是为什么,例如,为什么C++标准委员会把这个特性放进去,特别是考虑到C不允许EIT的事实。
for(int i=0; i<limit; i++)
...
for(int i=0; i<limit; ++i)
...
int i = 0;
int a = i++;
int i = 0;
int a = ++i;
i++   = 5;
i + 0 = 5;
int i; extern void f (int* p); extern void g (int& p); f (&++i); /* Would be illegal C, but C programmers havent missed this feature */ g (++i); /* C++ programmers would like this to be legal */ g (i++); /* Not legal C++, and it would be difficult to give this meaningful semantics */
int v = 0;
int const & rcv = ++v; // would work if ++v is an rvalue too
int & rv = ++v; // would not work if ++v is an rvalue
void taking_refc(int const& v);
taking_refc(10); // valid, 10 is an rvalue though!
Object o1(++a); // lvalue => can't steal. It will deep copy.
Object o2(a++); // rvalue => steal resources (like just swapping pointers)
for (int i = 0; i != 5; i++) {...}
mov i, eax
inc i