02小對象工具

如果需要保證類型T包含一個名爲value的常量或名爲type的類型,可以這樣寫:

template <typename T>
void myfunc()
{
typedef typename T::type ERROR_T_DOES_NOT_CONTAIN_type;
const int ASSERT_T_MUST_HAVE_STATIC_CONSTANT_value(T::value);
};

那麼如果傳進來的T沒有value或沒有type,就會報錯。

複雜的斷言通過不完整類型不能構造,或者如果T是不完整類型,sizeof(T)導致編譯錯誤來實現。

布爾斷言

template <bool STATEMENT>
struct static_assertion
{};
template <>
struct static_assertion<false>;

對false的特化沒有實現,如果傳入一個false值,將會報一個編譯期錯誤。

用宏封裝一下:

#define MXT_ASSERT(statement) sizeof(static_assertion<(statement)>)

宏的問題是逗號:

MXT_ASSERT(is_well_defined< std::map<int, double> >::value);

int的逗號前面的部分會識別爲宏的第一個參數,後面的部分會識別爲宏的第二個參數。

解決方法1:用typedef

typedef std::map<int, double> map_type;
MXT_ASSERT( is_well_defined<map_type>::value );

解決方法2:加括號

MXT_ASSERT(( is_well_defined< std::map<int, double> >::value ));

static_assertion可以繼承:

template <typename T>
class small_object_allocator : static_assertion<(sizeof(T)<64)>
{};

因爲static_assertion是一個類模板嘛。

斷言合法性

如果要檢查一些表達式的有效性,並且返回非void:

#define MXT_ASSERT_LEGAL(statement) sizeof(statement)

如果允許返回void:

#define MXT_ASSERT_LEGAL(statement) sizeof((statement), 0)//因爲void是不完整類型

如果要檢查T是否有一個函數empty:

template <typename T>
void do_something(T& x)
{
    MXT_ASSERT_LEGAL(static_cast<bool>(x.empty()));
    if (x.empty())
    {
        // ...
    }
}

其他的應用包括:

#define MXT_CONST_REF_TO(T) (*static_cast<const T*>(0))/**驗證解const引用合法,
                                                    static_cast<const T*>(0)得到一個const T*,
                                                    *static_cast<const T*>(0)是解引用,下面同理*/

#define MXT_REF_TO(T) (*static_cast<T*>(0))//驗證解引用合法
template <typename obj_t, typename iter_t>
class assert_iterator
{
    enum
    {
    verify_construction =
    MXT_ASSERT_LEGAL(obj_t(*MXT_CONST_REF_TO(iter_t))),	/**驗證拷貝構造函數合法
							MXT_CONST_REF_TO(iter_t)得到一個iter_t對象,
                                                        *MXT_CONST_REF_TO(iter_t)得到一個obj_t對象,
							obj_t(*MXT_CONST_REF_TO(iter_t))就是拷貝構造,下面同理*/
	verify_assignment =
	MXT_ASSERT_LEGAL(MXT_REF_TO(obj_t) = *MXT_CONST_REF_TO(iter_t)),//驗證賦值操作符合法
	verify_preincr =
	MXT_ASSERT_LEGAL(++MXT_REF_TO(iter_t)),//驗證前置自增合法
	verify_postincr =
	MXT_ASSERT_LEGAL(MXT_REF_TO(iter_t)++)//驗證後置自增合法
	};
};

用函數指針建模concept

template <typename T1, typename T2>
struct static_assert_can_copy_T1_to_T2
{
static void concept_check(T1 x, T2 y)
{
T2 z(x); // T2 must be constructable from T1
y = x; // T2 must be assignable from T1
}
static_assert_can_copy_T1_to_T2()
{
void (*f)(T1, T2) = concept_check;
}
};

通過派生或生成實例來使用

template <typename T>
T sqrt(T x)
{
static_assert_can_copy_T1_to_T2<T, double> CHECK1;
}
template <typename T>
class math_operations : static_assert_can_copy_T1_to_T2<T, double>
{};

這裏解釋一下。當生成CHECK1對象的時候,就會調用static_assert_can_copy_T1_to_T2的構造函數。類模板的成員函數在用到的時候纔會生成對應的代碼,構造函數裏用到concept_check,所以生成concept_check的代碼,並且檢查T2 z(x);y=x;的合法性。

標籤技術

在標準庫的例子如iterator。標籤的好處是構建臨時標籤對象代價很小,並且只編譯需要的功能。

類型標籤

struct naive_algorithm_tag {};
struct precise_algorithm_tag {};
template <typename T>
inline T log1p(T x, naive_algorithm_tag);
template <typename T>
inline T log1p(T x, precise_algorithm_tag);

也可以是模板標籤:

template <int N>
struct algorithm_precision_level {};
template <typename T, int N>
inline T log1p(T x, algorithm_precision_level<N>);
// ...
double x = log1p(3.14, algorithm_precision_level<4>());

函數標籤

enum algorithm_tag_t
{
NAIVE,
PRECISE
};
inline static_value<algorithm_tag_t, NAIVE> naive_algorithm_tag()
{
    return 0; 
}
inline static_value<algorithm_tag_t, PRECISE> precise_algorithm_tag()
{
    return 0;
}

標籤是函數指針,好處是可以爲相同的運行時和編譯時算法提供統一的語法。但感覺很少寫運行時+編譯時兩套實現,暫時跳過。

標籤迭代

用0填充數組:

template <typename T, int N>
void zeroize_helper(T* const data, static_value<int, N>)
{
zeroize_helper(data, static_value<int, N-1>());
data[N-1] = T();
}
template <typename T>
void zeroize_helper(T* const data, static_value<int, 1>)
{
data[0] = T();
}
template <typename T, int N>
void zeroize(T (&data)[N])
{
zeroize_helper(data, static_value<int, N>());
}

或者可以調換一下語句的順序:

template <typename T, int N>
void zeroize_helper(T* const data, static_value<int, N>)
{
data[N-1] = T();
zeroize_helper(data, static_value<int, N-1>());
}

這是線性的,也可以以指數的形式,假設N是2的冪:

template <int N, int M>
struct index
{
};
template <typename T, int N, int M>
void zeroize_helper(T* const data, index<N, M>)
{
zeroize_helper(data, index<N/2, M>());
zeroize_helper(data, index<N/2, M+N/2>());
}
template <typename T, int M>
void zeroize_helper(T* const data, index<1, M>)
{
data[M] = T();
}
template <typename T, int N>
void zeroize(T (&data)[N])
{
zeroize_helper(data, index<N, 0>());
}
double test[8];
zeroize(test);

展開如下:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章