三項演算子 ?:

?:演算子は、if-else文とよく似た働きをする。

if (/* 条件チェック */) {
    /* ... trueの場合に実行する式 */

} else {
    /* ... falseの場合に実行する式 */
}
D

if文は、trueの場合にはそのブロックを実行し、falseの場合にはそのブロックを実行する。ご存じのとおり、これは文であるため値はない。ifは、コードブロックの実行に影響を与えるだけだ。

一方、?:演算子は式だ。if-else文と同様に機能するほか、値を生成する。上記のコードと同等のものは、次の通りだ。

/* 条件 */ ? /* 真値式 */ : /* 偽値式 */
D

?:演算子は3つの式を使用するため、三項演算子と呼ばれる。

この演算子によって生成される値は、真式または偽式のいずれかの値になる。これは式であるため、式を使用できる場所ならどこでも使用できる。

次の例は、?:演算子とif-else文を比較している。これらの例のような場合、三項演算子はより簡潔だ。

上記の例からわかるように、特定の状況では、三項演算子を使用するとコードがより簡潔で明確になる。

三項式の型

?:演算子の値は、真式または偽式のいずれかの値になる。これら2つの式の型は同じである必要はないが、共通の型を持つ必要がある。

2つの式の共通型は、型変換や 継承を含む比較的複雑なアルゴリズムによって決定される。さらに、式に応じて、結果の種は l値またはr値のいずれかになる。これらの概念については、後の章で説明する。

ここでは、明示的な型変換を必要とせずに両方の値を表現できる型を共通型とみなしよう。例えば、整数型intlongは、どちらもlongとして表現できるため、共通型を持っている。一方、intstringは、intstringも自動的に他の型に変換できないため、共通型を持っていない。

式の型を簡単に決定する方法は、typeofを使用して、その.stringofプロパティを出力することだ。

int i;
double d;

auto result = someCondition ? i : d;
writeln(typeof(result).stringof);
D

doubleintを表現できるが、その逆は不可能であるため、上記の三項式式の共通型はdoubleになる。

double

共通型を持たない2つの式の例を見るために、出荷するアイテムの数を報告するメッセージを作成しよう。値が12の場合は"A dozen"と出力する。それ以外の場合は、正確な数をメッセージに含める。"3items will be shipped."

メッセージの変動部分を?:演算子で選択できると思うかもしれない:

writeln(
    (count == 12) ? "A dozen" : count, // ← コンパイルエラー
    " items will be shipped.");
D

残念ながら、式には共通の型がない。 "A dozen"stringで、countの型はintだから、式は共通の型を持っていない。

解決策は、まずcountstringに変換することだ。std.convモジュールにあるto!string関数は、指定されたパラメータからstring値を生成する。

import std.conv;
// ...
    writeln((count == 12) ? "A dozen" : to!string(count),
            " items will be shipped.");
D

これで、?:演算子の2つの選択式はどちらもstring型になったため、コードはコンパイルされ、期待どおりのメッセージが表示される。

演習

プログラムに、正の値は利益、負の値は損失を表す、純額として単一の int値を読み込ませよう。

プログラムは、金額が正か負かによって、"gained"または"lost"を含むメッセージを表示する必要がある。例えば、"$100 lost"または"$70 gained"などである。より適切であるかもしれないが、この演習ではif文は使用してはいけない。