C++范型編程 -- 頭文件


在type_traits頭文件中定義了許多非常好玩的東西,這里對着 c++ reference 翻譯一丟丟

 

一,helper class , std::intergral_constant

template< class T, T v >
struct integral_constant;

我們知道對在模板參數中的非類型參數必須為常量,所以這個東西就是可以為類型T的任意一個常量v,做出來一個特定的類型,即integral_constant<T, v>的實例。通常用來保存int 和 bool,它的實現是這樣的:

//聲明一個為bool偏特化的模板
template <bool B>
using bool_constant = integral_constant<bool, B>;

//這個偏特化的模板總共就能實例化出兩個具體的類型:
typedef std::integral_constant<bool, true> true_type typedef std::integral_constant<bool, false> false_type //可能的實現 template<class T, T v> struct integral_constant { static constexpr T value = v; typedef T value_type; typedef integral_constant type; constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } //since c++14 };

那么,在模板編程中,計算都是在編譯期搞定的, 作為模板參數的非類型參數必須為常量, 這也就說模板元編程中的變量一定是常量。。。如果要改變值只能在搞一個新的常量咯,

這個intergral_constant就能枚舉完 T 中的任何一個常量。

 

二、主要的類型分類,以下的每個類型都有一個value成員,如果判斷為真,則為true, 否則為false,這里挑幾個簡單介紹:

1, is_void, 用法 is_void<T>::value, 判斷T是否為void類型, 一看就是用來判斷函數返回值類型的

2, is_same<T1,T2> , 用法 is_same<T1, T2>::value, 判斷兩個類型是否一致,配合typedecl簡直不要太爽。。。不過得注意typedecl的引用

。。。。。剩下的略

 

三、enable_if

原型:

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

用法, 如果B為true, 則 enable_if<B, T>會有一個類型成員,名為type, 類型為 T, 配合上面的各種判斷,以及SFINAE 特性(無法實例化並不是錯誤),可以實現花式的自定義重載函數集:

 

// 1. the return type (bool) is only valid if T is an integral type:
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
  is_odd (T i) {return bool(i%2);}

// 2. the second template argument is only valid if T is an integral type:
template < class T,
           class = typename std::enable_if<std::is_integral<T>::value>::type> //  這么寫應該更容易明白: class T2 = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {return !bool(i%2);}

int main() {

  short int i = 1;    // code does not compile if type of i is not integral

  std::cout << std::boolalpha;
  std::cout << "i is odd: " << is_odd(i) << std::endl;
  std::cout << "i is even: " << is_even(i) << std::endl;

  return 0;
}

 

 或者更秀操作的這樣:

#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>{})
            >::type* = nullptr>
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
}

上面的代碼中,有一些會根據不同的條件來改變函數的簽名,而有的則不會, 具體使用哪種應該看情況使用,比如調整重載候選函數集合時應該主動改變函數簽名,而調整虛函數時則不應該改變函數簽名


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM