NF

地方で働くプログラマ

やさしいconstexpr(3)

やさしいconstexpr(1) - NF
やさしいconstexpr(2) - NF
時間経ってしまったけどつづき

関数にconstexprを指定する(つづき)

前回、constexpr関数を調べました。constexpr関数にすれば、戻り値をコンパイル時定数として扱えるよ、というメリットがありました。他になんかうれしい事あるの?と言う事で、constexpr を使うべき5の理由 - その3 - ボレロ村上 - ENiyGmaA Codeを読む。

参照透明でない関数は、以下のようにグローバルな状態を変更する可能性がある。
constexpr 関数であれば、副作用がないことを明示的に保証することができる。

早速試してみる。constexpr関数の中でグローバル変数を変更してみる。

#include <iostream>
size_t N = 10;
constexpr void foo(){ N = 11; }; 
int main() {
    foo();
    std::cout << N;
}

あれ…エラーになるのかと思ったけど、予想に反して普通にコンパイルも実行も出来てしまった。これだと、リンク先に書いてある「C:ドライブをフォーマットする実装」も出来てしまうように思える…
と言う訳で、理解してないとこういう疑問が出たりでなかったりする。(サンプル数1づつ)

そもそも副作用ってなに

一般的には意図した作用(=主作用)以外の作用が起こることで、風邪薬飲んだ時に眠くなるみたいな話だと思う。プログラミングにおいては、Wikipediaによると

評価値を得ること(※関数では「引数を受け取り値を返す」と表現する)が主たる作用とされ、それ以外のコンピュータの論理的状態(ローカル環境以外の状態変数の値)を変化させる作用を副作用という

らしい。つまり、意図していても状態を変化させたら副作用と呼ぶようだ。
副作用がありそうなサンプルを書いてみる。foo()は引数を2倍にして返す関数だけど、うっかり(?)メンバ変数Nを書き換えてしまっている、という例。

#include <iostream>
class MyArray {
public:
    MyArray(){ N = 0; }
    size_t foo(const size_t size){ N = size*2; return N; }  // 引数を2倍にして返す関数
    size_t N;
};
int main() {
    constexpr MyArray arr;
    std::cout << arr.N << std::endl;
    std::cout << arr.foo(10) << std::endl;  // メンバ変数Nも書き換えている
    std::cout << arr.N << std::endl;
}

ただ、このfoo()にconstexpr を付けても、やはり普通に実行できてしまう。
あかん、全然理解できてないので続く…