yohhoyの日記

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

TemplateParam = void

C++ライブラリのクラステンプレート設計で見られる、型テンプレートパラメータへのvoidデフォルト指定に関するメモ。

template<class T = void>
class SomeClass;

C++2b(C++23)標準ライブラリ時点では、下記パターンでの用法が存在する。

  • デフォルトvoid型:std::enable_ifC++11から)
  • ジェネリックメンバ関数std::plusなど(C++14から)
  • 型消去(type erasure):std::coroutine_handleC++20から)
  • 複雑なデフォルト型:std::generatorなど(C++2bから)

デフォルトvoid型

テンプレートパラメータ省略時のデフォルトをvoid型をとする、基本のパターン。

// <type_traits>
template<bool, class T = void> struct enable_if;

N4928 21.3.8.7より一部引用。

If B is true, the member typedef type denotes T; otherwise, there shall be no member type.

ジェネリックメンバ関数

クラステンプレートに対象型を明示する代わりに、関数呼び出しの実引数から型推論可能なメンバ関数テンプレートを提供する。

// <functional>
template<class T = void> struct plus;
// 同様に minus, multiplies, divides, modulus
// negate, equal_to, not_equal_to, greater, less, greater_equal, less_equal
// logical_and, logical_or, logical_not, bit_and, bit_or, bit_xor, bit_not
// <memory>のowner_less

C++14にて、2項演算や単項演算を行うファンクタクラス群で全面的に採用された。N4928 22.10.7.2より一部引用。

template<class T = void> struct plus {
  constexpr T operator()(const T& x, const T& y) const;
};
template<> struct plus<void> {
  template<class T, class U> constexpr auto operator()(T&& t, U&& u) const
    -> /*(snip)*/;
};

型消去(Type erasure)

具象型を指定したオブジェクトと、同オブジェクトから変換可能な型消去バージョンを提供する。

template<class Promise = void>
struct coroutine_handle;

例えばPromise型を明示したstd::coroutine_handle<MyPromise>からは、型消去されたstd::coroutine_handle<>へと暗黙変換可能。N4928 17.12.4.1より一部引用。

template<>
struct coroutine_handle<void> {
  // (snip)
};

template<class Promise>
struct coroutine_handle {
  // (snip)
  constexpr operator coroutine_handle<>() const noexcept;
  // (snip)
};

複雑なデフォルトパラメータ

他テンプレートパラメータに基づくデフォルトの型を、(テンプレート宣言では記述できない)複雑なルールで導出する。

// <generator>
template<class Ref, class V = void, class Allocator = void>
class generator;
// <memory>
template<class Pointer = void, class Smart, class... Args>
auto out_ptr(Smart& s, Args&&... args);
template<class Pointer = void, class Smart, class... Args>
auto inout_ptr(Smart& s, Args&&... args);

例えばジェネレータコルーチン戻り値型std::generator(→id:yohhoy:20220801)では、デフォルトの参照型/値型/アロケータ型指定のために本パターンを利用する。N4928 26.8.3より一部引用。

template<class Ref, class V = void, class Allocator = void>
class generator : public ranges::view_interface<generator<Ref, V, Allocator>> {
private:
  using value = conditional_t<is_void_v<V>, remove_cvref_t<Ref>, V>;
  using reference = conditional_t<is_void_v<V>, Ref&&, Ref>;
  // (snip)
};