エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

遅延評価if

C++のテンプレートで。

Haskellのばあい

基本に立ち返って、まずはHaskellのばあい。
Haskellは基本的に遅延評価なので、不適切な式が含まれていても評価されないのであれば全体としては正しく評価してくれます。
ここでinfinityを評価してしまうと無限再帰呼び出しになってしまいますが、if_に与えられるconditionがFalseであるかぎりinfinityは評価さません。結果としてif_は値を返すことができます。

if_ condition m n = if condition then m else n

infinity = infinity + 1

main = print $ if_ False infinity 1

C++のテンプレートで失敗したばあい

先日、Haskellと同じ調子で次のように書いてしまいました。

template<bool F, int M, int N> struct If { static const int value = M; };
template<int M, int N> struct If<false, T, U> { static const int value = N; };

template<int N = 0> struct Infinity { static const int value = Infinity<N + 1>::value; };

int n = If<false, Infinity<>::value, 1>::value;


もう少しだけ複雑な式だったため、なぜエラーになるのかわからず半日ほど考えてしまう始末。

落ち着いて考えてみれば自明なのですが、Ifに値を与えるばあい、MNの値は確定していなければなりません。Mの値、Nの値を含めたIf<F, M, N>全体が型なので当然の結果でした。

C++のテンプレートでやりなおし

数値を直接テンプレートに渡してしまったための過ちなので、テンプレートには型を与えて、その型から数値を取得するように書き換えます。

template<bool F, typename T, typename U> struct If { static const int value = T::value; };
template<typename T, typename U> struct If<false, T, U> { static const int value = U::value; };

template<int N = 0> struct Infinity { static const int value = Infinity<N + 1>::value; };

template<int N> struct Int { static const int value = N; };

int n = If<false, Infinity<>, Int<1> >::value;


このC++テンプレートを実行してみます。OS は OS X Mavericks 、実行環境は G++ 5.1 です。

$ g++ -S lazy_if.cpp 
$ cat lazy_if.s 
	.section	__TEXT,__text,regular,pure_instructions
	.section	__DATA,__data
	.globl	_n                      ## @n
	.align	2
_n:
	.long	1                       ## 0x1


Infinityテンプレートに惑わされず、nに1が入ることが確認できました(爆)。