条件演算子の戻り値にはthrow-expressionを書くことが出来る
5.16 Conditional operator
If either the second or the third operand has type void, then the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the second and third operands, and one of the following shall hold:
— The second or the third operand (but not both) is a throw-expression (15.1); the result is of the type of the other and is a prvalue.
— Both the second and the third operands have type void; the result is of type void and is a prvalue.
[ Note: This includes the case where both operands are throw-expressions. — end note ]
大学で小さな勉強会を開いたのですが、条件演算子の戻り値となる部分にthrowを書くことが出来るというのは意外と知られていませんでした。
良い機会だったので、帰宅してから規格を確認し直しました。
条件演算子の戻り値部分(二項め又は三項め)がthrowであった場合、他方が型を持っていれば、戻り値の型は他方の型になります。
両方がthrowであれば、戻り値の型はvoid型となります。
実用例
#include <type_traits> #include <stdexcept> #include <cassert> template <class T, std::size_t N> struct ctarray { T value[N]; constexpr T operator[](const std::size_t n) const { return value[n]; } constexpr T at(const std::size_t n) const { return n < N ? value[n] : throw std::out_of_range("out of range"); } constexpr std::size_t length() const { return N; } constexpr std::size_t size() const { return N; } }; int main() { constexpr ctarray<int, 5> tmp{{0, 1, 2, 3, 4}}; static_assert(tmp.at(0) == 0, ""); assert(tmp.at(0) == 0); }
このようなコンパイル時、実行時両方で使える配列型を定義するとします。
atメンバ関数は範囲チェック付きの要素アクセスを行う関数です。
範囲チェックにstatic_assertを用いた場合、atメンバ関数はコンパイル時には使えますが、実行時に使えなくなってしまいます。
条件演算子を用いて範囲外の場合にout_of_rangeをthrowするようにすれば、コンパイル時にはコンパイルエラー、実行時には例外にすることが出来ます。
C++14以降では条件チェックにif文などを使う事も出来ますが、条件演算子からthrow出来る事は知っておくべきだと思います。