まえおき
筆者はC90からC++14にワープしてきた、C++自体の勉強をはじめて間もない、しがない者です。間違いを含む可能性が大いにありますので、詳細については規格票を参照下さい(ちなみに僕が参照しているドラフトはN4296です)。
何卒、何卒、宜しくお願い申し上げます。
環境
$ 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.
$ gcc -std=c90 -pedantic -Wall -o sample sample.c
$ g++ -std=c++14 -pedantic -Wall -o sample sample.cpp
では本題
C90では、評価結果が左辺値となる演算子は単項*
、[]
、.
、->
の4種類ぐらいだったように記憶しています。ですので、以下のコード(右辺値に代入を試みる)はコンパイルすると全滅します。
# 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;
}
条件演算子・コンマ演算子・代入演算子の式を代入先に指定するには、ポインタを介して無理矢理左辺値に変換する必要がありました。
# 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となりうることを知り、仰天しました。
# 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の順序関係はこうなるんじゃないかなという僕の考えを示します。
- 左/右オペランドのvalue computationが行われる(式
++m
は式m+=1
、すなわち式m=m+1
と等価なので、この式の各オペランドに対してもvalue computationが行われるが、煩雑になるので省略します)。 - 式
++m
が計算された結果として、m
へのlvalue参照を返す。 - 代入(更新後の
m
へ55を代入)が行われる。 - 代入式のvalue computationが行われ、
m
へのlvalue参照を返す。
となるので、部分式++m
にともなう副作用は、代入時点ですでに完了していることが保証されているようです。
(あ、でも規格票の読込みがまだ甘いので、解釈が間違っていたらすみません...)
以上です。