名稱空間
變體
操作

std::enable_if

來自 cppreference.com
< cpp‎ | 型別
 
 
超程式設計庫
型別特性
型別類別
(C++11)
(C++11)(DR*)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
(C++11) 
(C++11)
(C++11)
型別屬性
(C++11)
(C++11)
(C++14)
(C++11)(C++26 中已棄用)
(C++11)(直到 C++20*)
(C++11)(C++20 中已棄用)
(C++11)
型別特性常量
元函式
(C++17)
支援的操作
關係與屬性查詢
型別修改
(C++11)(C++11)(C++11)
型別轉換
(C++11)(C++23 中已棄用)
(C++11)(C++23 中已棄用)
(C++11)
(C++11)(直到 C++20*)(C++17)

enable_if
(C++11)
(C++17)
編譯時有理數算術
編譯時整數序列
 
定義於標頭檔案 <type_traits>
template< bool B, class T = void >
struct enable_if;
(C++11 起)

如果 Btrue,則 std::enable_if 有一個公共成員 typedef type,等於 T;否則,沒有成員 typedef。

在 C++20 的概念出現之前,此元函式是利用 SFINAE 的便捷方式,特別是用於根據型別特性有條件地從候選集中移除函式,從而允許基於這些不同型別特性進行獨立的函式過載或特化。

std::enable_if 可以以多種形式使用,包括:

  • 作為額外的函式引數(不適用於大多數運算子過載),
  • 作為返回型別(不適用於建構函式和解構函式),
  • 作為類模板或函式模板引數。

如果程式為 std::enable_if 新增特化,則行為未定義。

目錄

[編輯] 成員型別

型別 定義
型別 根據 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 } 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) {} // error: treated as redefinition
};
 
/* RIGHT */
 
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
};

在名稱空間作用域函式模板的模板非型別引數的型別中使用 enable_if 時應小心。一些 ABI 規範(如 Itanium ABI)不包括非型別模板引數的例項化依賴部分在名稱修飾中,這意味著兩個不同函式模板的特化最終可能會得到相同的修飾名並被錯誤地連結在一起。例如:

// 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 <iostream>
#include <new>
#include <string>
#include <type_traits>
 
namespace detail
{ 
    void* voidify(const volatile void* ptr) noexcept { return const_cast<void*>(ptr); } 
}
 
// #1, enabled via the return type
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";
}
 
// same as above
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, enabled via a parameter
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, enabled via a non-type template parameter
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 << "destroying non-trivially destructible T\n";
    t->~T();
}
 
// #5, enabled via a type 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()
{
    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: matches the primary template
    A<double>{}; // OK: matches the partial specialization
}

輸出

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

[編輯] 另請參閱

(C++17)
void 可變引數別名模板
(別名模板)[編輯]