yohhoyの日記

技術的メモをしていきたい日記

conjunction/disjunctionと短絡インスタンス化

C++1z(C++17)標準ライブラリで追加される std::conjunction, std::disjunction メタ関数では、型特性(type traits)のテンプレートインスタンス化が短絡評価(short-circuiting)される。通常の&&演算子, ||演算子では各項の真偽値によらず全てのインスタンス化が行われてしまうため、テンプレートメタプログラミングの文脈では conjunction, disjunction メタ関数の方が使い勝手が良い。

  • conjunctionメタ関数:型特性に対する論理積(AND); 短絡評価
  • disjunctionメタ関数:型特性に対する論理和(OR); 短絡評価
  • negationメタ関数:型特性に対する論理否定(NOT)

ある型Tに対し「std::atomic<T>が常にロックフリー(lock free)か否か」を判定するメタ関数は、std::conjunctionを利用して下記の通りに記述できる。*1

// C++1z(C++17)
#include <atomic>
#include <type_traits>

template <typename T>
struct is_lock_free_impl
  : std::bool_constant<std::atomic<T>::is_always_lock_free> { };

template <typename T>
using is_lock_free
  = std::conjunction<std::is_trivially_copyable<T>, is_lock_free_impl<T>>;  // ★

メタ関数is_lock_freeの定義で通常の&&演算子を使った場合、左辺std::is_trivially_copyable<T>::valueの真偽値に関わらず右辺is_lock_free_impl<T>::valueインスタンス化が行われる。std::atomic<T>はクラステンプレートの要件としてTrivially Copyableな型Tを要求するため*2、下記コードではメタ関数is_lock_free_implインスタンス化の過程でコンパイルエラーとなってしまう。

template <typename T>
struct is_lock_free : std::bool_constant<
  std::is_trivially_copyable<T>::value && is_lock_free_impl<T>::value  // ★ &&演算子
> { };

// (std::atomic<int>型がロックフリーに振る舞う処理系を仮定)
static_assert( is_lock_free<int>::value, "int is lock-free" );  // OK

// 非Trivially Copyableなクラス型X
struct X { ~X() {} };
static_assert( !is_lock_free<X>::value, "X is not lock-free" );  // NG: ill-formed

N4659(C++1z DIS) 23.15.8/p2-3, p7-8より引用。

template<class... B> struct conjunction : see below { };
2 The class template conjunction forms the logical conjunction of its template type arguments.
3 For a specialization conjunction<B1, ..., BN>, if there is a template type argument Bi for which bool(Bi::value) is false, then instantiating conjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i. [Note: This is analogous to the short-circuiting behavior of the built-in operator &&. --end note]

template<class... B> struct disjunction : see below { };
7 The class template disjunction forms the logical disjunction of its template type arguments.
8 For a specialization disjunction<B1, ..., BN>, if there is a template type argument Bi for which bool(Bi::value) is true, then instantiating disjunction<B1, ..., BN>::value does not require the instantiation of Bj::value for j > i. [Note: This is analogous to the short-circuiting behavior of the built-in operator ||. --end note]

関連URL

*1:std::bool_constant, std::is_always_lock_free はいずれもC++1z標準ライブラリで追加される。

*2:N4659 32.6/p1: "The template argument for T shall be trivially copyable."