意外にもlvalueを結果とする演算子（ふっしぎー）

#まえおき

筆者はC90からC++14にワープしてきた、C++自体の勉強をはじめて間もない、しがない者です。間違いを含む可能性が大いにありますので、詳細については規格票を参照下さい（ちなみに僕が参照しているドラフトは**N4296**です）。

何卒、何卒、宜しくお願い申し上げます。

#環境

```bash:
$ uname -a
Linux ubuntu 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.1 LTS
Release:        16.04
Codename:       xenial
$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

```bash:コンパイルオプション（C90）
$ gcc -std=c90 -pedantic -Wall -o sample sample.c
```

```bash:コンパイルオプション（C++14）
$ g++ -std=c++14 -pedantic -Wall -o sample sample.cpp
```

#では本題
**C90**では、評価結果が左辺値となる演算子は単項`*`、`[]`、`.`、`->`の4種類ぐらいだったように記憶しています。ですので、以下のコード（右辺値に代入を試みる）はコンパイルすると全滅します。

```c:×C90ではill-formed
#include <stdio.h>

int main(void)
{
    int i,j,k,l,m;

    (1 ? i : j) = 11; /* NG: lvalue required as left operand of assignment */
    (i, j) = 22;      /* NG: lvalue required as left operand of assignment */
    (k = 0) = 33;     /* NG: lvalue required as left operand of assignment */
    l = 0;
    (l+=0) = 44;      /* NG: lvalue required as left operand of assignment */
    m = 0;
    ++m = 55;         /* NG: lvalue required as left operand of assignment */

    return 0;
}
```

条件演算子・コンマ演算子・代入演算子の式を代入先に指定するには、ポインタを介して無理矢理左辺値に変換する必要がありました。

```c:○well-formed
#include <stdio.h>

int main(void)
{
    int i, j, k, l, m;

    *(1 ? &i : &j) = 11; /* OK */
    *(i, &j) = 22;       /* OK */
    *(k = 0, &k) = 33;   /* OK */
    l = 0;
    *(l+=0, &l) = 44;    /* OK */
    m = 0;
    *(++m, &m) = 55;     /* OK */

    printf("%d %d %d %d %d\n", i, j, k, l, m); 
    /* => 11 22 33 44 55 */

    return 0;
}
```

#C++14では...

しかしC++14では、これらの演算子の評価結果のvalue categoryが**lvalue**となりうることを知り、仰天しました。

```c++:○well-formed
#include <iostream>
#include <cstdio>

int main()
{
    int i,j,k,l,m,n;

    (true?i:j) = 11; 
    (i,j) = 22; 
    (k=0) = 33; 
    l = 0;
    (l+=0) = 44; 
    m = 0;
    ++m = 55; 
    n = 0;
    --n = 66; 

    printf("%d %d %d %d %d %d\n", i, j, k, l, m, n); 
    /* => 11 22 33 44 55 66 */
    return 0;
} 
```

すごいですね。「なりうる」と書いたように、もちろん、コンマ演算子の結果のvalue categoryは右オペランドのvalue categoryとなるなど条件はあるみたいですが。

なお、後置`++`/`--`の評価結果のvalue categoryはcv-unqualifiedの**prvalue**となるため、このような代入はできないようです（cf.N4296 §5.2.6 Increment and decrement）。

特に、条件演算子の評価結果のvalue categoryを決定するプロセスは非常に複雑みたいですね。C90も複雑気味でしたが、C++14ほどではないです。

#副作用完了の順序性は大丈夫なの？
とは、やはり個人的に気になったところです。C90では副作用完了点のひとつは式文中の完全式の終わりですが、C++ではどうか？（以下、N4296より引用。太字引用者）

>**5.18 Assignment and compound assignment operators**
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. **In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.** With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation. 

式文`++m = 55;`のvalue computationの順序関係はこうなるんじゃないかなという僕の考えを示します。

1. 左/右オペランドのvalue computationが行われる（式`++m`は式`m+=1`、すなわち式`m=m+1`と等価なので、この式の各オペランドに対してもvalue computationが行われるが、煩雑になるので省略します）。
2. 式`++m`が計算された結果として、`m`へのlvalue参照を返す。
3. 代入（更新後の`m`へ55を代入）が行われる。
4. 代入式のvalue computationが行われ、`m`へのlvalue参照を返す。

となるので、部分式`++m`にともなう副作用は、代入時点ですでに完了していることが保証されているようです。

（あ、でも規格票の読込みがまだ甘いので、解釈が間違っていたらすみません...）

以上です。