std:: enable_if
| Type traits | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Compile-time rational arithmetic | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Compile-time integer sequences | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
(C++14)
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
ヘッダーで定義
<type_traits>
|
||
|
template
<
bool
B,
class
T
=
void
>
struct enable_if ; |
(C++11以降) | |
B
が
true
の場合、
std::enable_if
は
T
と等しい公開メンバーtypedef
type
を持つ。それ以外の場合、メンバーtypedefは存在しない。
このメタ関数は、C++20の concepts 以前の SFINAE を活用する便利な方法であり、特に型特性に基づいて candidate set から関数を条件付きで削除し、異なる型特性に基づいて個別の関数オーバーロードや特殊化を可能にするものです。
std::enable_if
は、以下の形式を含む多くの形式で使用できます:
- 追加の関数引数として(ほとんどの演算子オーバーロードには適用されません)、
- 戻り値の型として(コンストラクタとデストラクタには適用されません)、
- クラステンプレートまたは関数テンプレートのパラメータとして。
プログラムが
std::enable_if
に対する特殊化を追加する場合、動作は未定義です。
目次 |
メンバー型
| 型 | 定義 |
type
|
B
の値に応じて、
T
またはそのようなメンバーが存在しない
|
ヘルパー型
|
template
<
bool
B,
class
T
=
void
>
using enable_if_t = typename enable_if < B,T > :: type ; |
(C++14以降) | |
実装例
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; }; |
タグ内のC++コード、C++専門用語は翻訳せず、元のフォーマットを保持しています。翻訳対象となるテキストが存在しないため、出力は入力と同一です)
注記
よくある間違いは、デフォルトテンプレート引数のみが異なる2つの関数テンプレートを宣言することです。これは機能しません。なぜなら、これらの宣言は同じ関数テンプレートの再宣言として扱われるからです(デフォルトテンプレート引数は 関数テンプレートの等価性 において考慮されません)。
/* 誤り */ struct T { enum { int_t, float_t } type; template<typename Integer, typename = std::enable_if_t<std::is_integral<Integer>::value>> T(Integer) : type(int_t) {} template<typename Floating, typename = std::enable_if_t<std::is_floating_point<Floating>::value>> T(Floating) : type(float_t) {} // エラー: 再定義として扱われる }; /* 正しい */ struct T { enum { int_t, float_t } type; template<typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true> T(Integer) : type(int_t) {} template<typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true> T(Floating) : type(float_t) {} // OK };
namespaceスコープの関数テンプレートの定数テンプレートパラメータの型で
enable_if
を使用する際には注意が必要です。Itanium ABIのような一部のABI仕様では、マングリングに定数テンプレートパラメータのインスタンス化依存部分を含めないため、2つの異なる関数テンプレートの特殊化が同じマングル名になり、誤ってリンクされてしまう可能性があります。例:
// 最初の翻訳単位 struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value1, int> = 0> void func() {} // #1 template void func<X>(); // #2 // 2番目の翻訳単位 struct X { enum { value1 = true, value2 = true }; }; template<class T, std::enable_if_t<T::value2, int> = 0> void func() {} // #3 template void func<X>(); // #4
関数テンプレート #1 と #3 は異なるシグネチャを持ち、別個のテンプレートです。しかしながら、#2 と #4 は、異なる関数テンプレートのインスタンス化であるにもかかわらず、
Itanium C++ ABI では
同じマングル名 (
_Z4funcI1XLi0EEvv
) を持つため、リンカーは誤ってこれらを同一のエンティティと見なすことになります。
例
#include <iostream> #include <new> #include <string> #include <type_traits> namespace detail { void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } } // #1, 戻り値型で有効化 template<class T> typename std::enable_if<std::is_trivially_default_constructible<T>::value>::type construct(T*) { std::cout << "trivially default constructibleなTのデフォルト構築\n"; } // 同上 template<class T> typename std::enable_if<!std::is_trivially_default_constructible<T>::value>::type construct(T* p) { std::cout << "非自明なデフォルト構築可能なTのデフォルト構築\n"; ::new(detail::voidify(p)) T; } // #2 template<class T, class... Args> std::enable_if_t<std::is_constructible<T, Args&&...>::value> // ヘルパー型の使用 construct(T* p, Args&&... args) { std::cout << "Tを操作で構築中\n"; ::new(detail::voidify(p)) T(static_cast<Args&&>(args)...); } // #3, パラメータ経由で有効化 template<class T> void destroy( T*, typename std::enable_if< std::is_trivially_destructible<T>::value >::type* = 0) { std::cout << "自明に破棄可能な T を破棄中\n"; } // #4, 定数テンプレートパラメータで有効化 template<class T, typename std::enable_if< !std::is_trivially_destructible<T>{} && (std::is_class<T>{} || std::is_union<T>{}), bool>::type = true> void destroy(T* t) { std::cout << "非自明なデストラクタを持つTの破棄\n"; t->~T(); } // #5, 型テンプレートパラメータを介して有効化 template<class T, typename = std::enable_if_t<std::is_array<T>::value>> void destroy(T* t) // 注: 関数シグネチャは変更されていません { for (std::size_t i = 0; i < std::extent<T>::value; ++i) destroy((*t)[i]); } /* template<class T, typename = std::enable_if_t<std::is_void<T>::value>> void destroy(T* t) {} // エラー: #5と同じシグネチャを持つ */ // Aの部分特殊化はテンプレートパラメータを介して有効化される template<class T, class Enable = void> class A {}; // プライマリテンプレート template<class T> class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {}; // 浮動小数点型の特殊化 int main() { union { int i; char s[sizeof(std::string)]; } u; construct(reinterpret_cast<int*>(&u)); destroy(reinterpret_cast<int*>(&u)); construct(reinterpret_cast<std::string*>(&u), "Hello"); destroy(reinterpret_cast<std::string*>(&u)); A<int>{}; // OK: プライマリテンプレートに一致 A<double>{}; // OK: 部分特殊化に一致します }
出力:
default constructing trivially default constructible T destroying trivially destructible T constructing T with operation destroying non-trivially destructible T
関連項目
|
(C++17)
|
void可変長エイリアステンプレート
(エイリアステンプレート) |