# std::enable_if

< cpp‎ | types

定义于头文件 `<type_traits>` |  |
template< bool B, class T = void > struct enable_if; |  | (C++11 起)

若 `B` 为 true ，则 `std::enable_if` 拥有等同于 `T` 的公开成员 typedef `type` ；否则，无该成员 typedef 。

此元函数是活用 SFINAE ，基于类型特性条件性地从重载决议移除函数，并对不同类型特性提供分离的函数重载与特化的便利方法。 `std::enable_if` 可用作额外的函数参数（不可应用于运算符重载）、返回类型（不可应用于构造函数与析构函数），或类模板或函数模板形参。

添加 `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; };

### 注解

常见错误是声明二个函数模板，而它们仅于其默认模板实参相异。这是无效的，因为这些函数声明被当做同一函数模板的再声明（默认模板实参不为函数模板等价所考虑）。

    /* WRONG */

    struct T {
        enum { int_t,float_t } m_type;
        template <typename Integer,
                  typename = std::enable_if_t<std::is_integral_v<Integer>>
        >
        T(Integer) : m_type(int_t) {}

        template <typename Floating,
                  typename = std::enable_if_t<std::is_floating_point_v<Floating>>
        >
        T(Floating) : m_type(float_t) {} // 错误：不能重载
    };

    /* RIGHT */

    struct T {
        enum { int_t,float_t } m_type;
        template <typename Integer,
                  std::enable_if_t<std::is_integral_v<Integer>, int> = 0
        >
        T(Integer) : m_type(int_t) {}

        template <typename Floating,
                  std::enable_if_t<std::is_floating_point_v<Floating>, int> = 0
        >
        T(Floating) : m_type(float_t) {} // OK
    };

于命名空间函数模板作用域的模板非类型形参中使用 `enable_if` 时需留意。某些 ABI 规范，如 Itanium ABI ，不于重整中包含非类型模板形参的实例化依赖部分。这表示可能以相同重整名归纳二个相异的函数模板特化，并且错误地相互链接。例如：

    // 第一个翻译单元

    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

    // 第二个翻译单元

    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 <new>
    #include <iostream>
    #include <string>

    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 << "default constructing 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 << "default constructing non-trivially default constructible T\n";
        ::new(detail::voidify(p)) T;
    }

    // #2
    template<class T, class... Args>
    std::enable_if_t<std::is_constructible<T, Args&&...>::value> // Using helper type
        construct(T* p, Args&&... args)
    {
        std::cout << "constructing T with operation\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 << "destroying trivially destructible T\n";
    }

    // #4 ，经由非类型模板形参启用
    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 ，经由类型模板形参启用
    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()
    {
        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>{}; // OK ：匹配初等模板
        A<double>{}; // OK ：匹配偏特化+
    }

输出：

    default constructing trivially default constructible T
    destroying trivially destructible T
    constructing T with operation
    destroying non-trivially destructible T

### 参阅

void_t (C++17) | `void` 变参别名模板 (别名模板)

- static_assert
- SFINAE