組み込み型の一時オブジェクトは変更不可らしい

Exceptional C++を読んでたら
"C++ language doesn't allow you to modify temporaries of builtin type."
(適当訳:組み込み型の一時オブジェクトは変更不可)
という一文があったのでいろいろ試してみた。


まず

int func1()
{
	return 1;
}

int main()
{
	std::cout << ++func1() << std::endl;
	return 0;
}

をやってみると
error C2105: '++' には左辺値が必要です。
となって確かにコンパイルできない


次にユーザー定義クラスで試してみると

struct Test
{
	int arg;
	Test(int i) : arg(i){}
	Test& operator+(const int i)
	{
		arg += i;
		return *this;
	}

	Test& operator++()
	{
		arg++;
		return *this;
	}

	friend std::ostream& operator<<(std::ostream& os,Test &obj)
	{
		os << obj.arg;
		return os;
	}
};

Test func2()
{
	return Test(1);
}

int main()
{
	std::cout << ++func2() << std::endl;
	return 0;
}

これだとユーザー定義クラスなので通る


ここで、さっきのfunc1がコンパイルに失敗したときのエラーメッセージを思い出してみると
error C2105: '++' には左辺値が必要です。
となっていたので、確か左辺値って代入可能な値だったよなーということで
intのリファレンスを返す関数ならどうだろうと試してみると

struct Test
{
	int arg;
	int& refArg;
	Test(int i) : arg(i) , refArg(arg){}
	Test& operator+(const int i)
	{
		arg += i;
		return *this;
	}

	Test& operator++()
	{
		arg++;
		return *this;
	}

	friend std::ostream& operator<<(std::ostream& os,Test &obj)
	{
		os << obj.arg;
		return os;
	}

	int& memIntFun()
	{
		return arg;
	}
};

int main()
{
	std::cout << ++obj.memIntFun() << std::endl;
	return 0;
}

これだと大丈夫らしい。
ちなみに、ユーザー定義クラスのリファレンスを返すような関数で得られた一時オブジェクトも、もちろん変更可能。


この辺をまとめてみると
どうやら「基本型の一時オブジェクトは変更不可」というよりも
単純に基本型の値返しは右辺値になって、右辺値は変更不可だから、結果として変更不可になる。
って意味っぽい。


ここまでやって、ユーザー定義クラスも非リファレンスの値返しは右辺値でいいんじゃないの?って思ったんだけど

int main()
{
	//こういう意味のないコードもユーザー定義クラスは左辺値扱いなので通る
	std::cout << ++(func2() = Test(4)) << std::endl;
	return 0;
}

ユーザー定義クラスだとオペレータオーバーロードされた場合に中で何をしてるかよくわからないしとりあえず左辺値 ってことなのかな


まあなにより左辺値と右辺値のイメージが曖昧すぎて何か間違ってる気がしなくもないんだけど