名前空間
変種
操作

std::enable_if

提供: cppreference.com
< cpp‎ | types
 
 
 
型サポート
型プロトタイプ
(C++11)
(C++11)
(C++14)
(C++11)
(C++11)(C++20以前)
(C++11)(C++20で非推奨)
(C++11)
型特性定数
メタ関数
(C++17)
エンディアン
(C++20)
サポートされている操作
関係と性質の問い合わせ
(C++11)
(C++11)
型変更
(C++11)(C++11)(C++11)
型変換
(C++11)
enable_if
(C++11)
(C++17)
(C++11)(C++20以前)(C++17)
 
ヘッダ <type_traits> で定義
template< bool B, class T = void >
struct enable_if;
(C++11およびそれ以降)

Btrue であれば、 std::enable_ifT に等しいパブリックメンバ型 type を持ちます。 そうでなければ、メンバ型は存在しません。

このメタ関数は、型特性に基づいて条件的にオーバーロード解決から関数を削除したり、異なる型特性に対して異なる関数のオーバーロードや特殊化を提供するために、 SFINAE を利用するための便利な方法です。 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; };

[編集] ノート

よくある間違いは、デフォルトテンプレート引数のみが異なる2つの関数テンプレートを宣言することです。 これは不正です。 デフォルトテンプレート引数は関数テンプレートのシグネチャの一部ではなく、同じシグネチャを持つ2つの異なる関数テンプレートの宣言は不正であるためです。

struct T {
    enum { int_t,float_t } m_type;
    template <typename Integer,
              typename = std::enable_if_t<std::is_integral<Integer>::value>
    >
    T(Integer) : m_type(int_t) {}
 
    template <typename Floating,
              typename = std::enable_if_t<std::is_floating_point<Floating>::value>
    >
    T(Floating) : m_type(float_t) {} // error: cannot overload
};

名前空間スコープの関数テンプレートのテンプレート非型引数の型に enable_if を使用するときは注意が必要です。 Itanium ABI のような一部の ABI の仕様は、マングリングにおける非型テンプレート引数の実体化依存の部分を含みません。 これは、2つの異なる関数テンプレートの特殊化が同じマングリング名になり、誤ってリンクされるかもしれないということを意味します。 例えば、

// first translation unit
 
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
 
// second translation unit
 
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 <type_traits>
#include <iostream>
#include <string>
 
namespace detail { struct inplace_t{}; }
void* operator new(std::size_t, void* p, detail::inplace_t) {
    return p;
}
 
// #1, enabled via the return type
template<class T,class... Args>
typename std::enable_if<std::is_trivially_constructible<T,Args&&...>::value>::type 
    construct(T* t,Args&&... args) 
{
    std::cout << "constructing trivially constructible T\n";
}
 
// #2
template<class T, class... Args>
std::enable_if_t<!std::is_trivially_constructible<T,Args&&...>::value> //Using helper type
    construct(T* t,Args&&... args) 
{
    std::cout << "constructing non-trivially constructible T\n";
    new(t, detail::inplace_t{}) T(args...);
}
 
// #3, enabled via a parameter
template<class T>
void destroy(T* t, 
             typename std::enable_if<std::is_trivially_destructible<T>::value>::type* = 0) 
{
    std::cout << "destroying trivially destructible T\n";
}
 
// #4, enabled via a template parameter
template<class T,
         typename std::enable_if<
             !std::is_trivially_destructible<T>{} &&
             (std::is_class<T>{} || std::is_union<T>{}),
            int>::type = 0>
void destroy(T* t)
{
    std::cout << "destroying non-trivially destructible T\n";
    t->~T();
}
 
// #5, enabled via a template parameter
template<class T,
	typename = std::enable_if_t<std::is_array<T>::value> >
void destroy(T* t) // note, function signature is unmodified
{
    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){} // error: has the same signature with #5
*/
 
// the partial specialization of A is enabled via a template parameter
template<class T, class Enable = void>
class A {}; // primary template
 
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types
 
int main()
{
    std::aligned_union_t<0,int,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> a1; // OK, matches the primary template
    A<double> a2; // OK, matches the partial specialization
}

出力:

constructing trivially constructible T
destroying trivially destructible T
constructing non-trivially constructible T
destroying non-trivially destructible T

[編集] 関連項目

(C++17)
void の可変個引数エイリアステンプレート
(エイリアステンプレート) [edit]