2015-07-03 15 views
4

我希望能夠檢查元程序中使用的類的不變量。我的第一個幼稚的做法是在元編程中強制實現不變量

template <int N> 
struct digit 
{ 
    static_assert((N >= 0) && (N < 10), "bad invariant"); 
}; 

using boom = digit<99>; 

但是,這個編譯沒有任何問題。只有在構造非法類時,纔會觸發靜態斷言。

增加額外的模板參數時,它是可能的:

#include <type_traits> 

template <int N, 
      typename = typename std::enable_if<(N >= 0) && (N < 10)>::type> 
struct digit; 

using crash = digit<-7>; 

當我想這種技術適用於作爲一個類型列表類:

#include <type_traits> 

template <typename ...> struct are_integral; 

template <typename T, typename ...Ts> 
struct are_integral<T, Ts...> 
{ 
    static const bool value = std::is_integral<T>::value && 
          are_integral<Ts...>::value; 
}; 

template <> 
struct are_integral<> : std::true_type { }; 



template <typename ...Ts, 
      typename = typename std::enable_if<are_integral<Ts...>::value>::type> 
struct list; 

using ok = list<int, long, char>; 
using bad = list<double>; 

它根本不工作,因爲海灣合作委員會抱怨,

error: parameter pack 'Ts' must be at the end of the template parameter list struct list;

即使它會工作,類是usel因爲模板參數包不能反映類型列表。

於是,我就用一個「非法」的基類:

template <typename> struct check; 

template <typename ...Ts> 
struct list : check<typename std::enable_if<are_integral<Ts...>::value>::type> 
{ }; 

using ok = list<int, long, char>; 
using bad = list<double>; 

這編譯沒有問題。

有什麼辦法可以在C++ 11中完成類似的事情,還是我必須等待概念?

+0

如果您不介意間接性,您可以使用別名模板而不是直接引用「list」模板。不過,我不認爲這是一個很好的解決方案。 – dyp

+0

相關問題我剛纔問:http://stackoverflow.com/questions/11251569/should-static-assert-be-triggered-with--typedef。正如你所看到的,這個問題與模板實例化有關,它儘可能延遲編譯器。目前,我無法想出一種在未明確訪問類成員之一的情況下觸發實例化的方法,但我會繼續思考! –

回答

1

您的問題出現了,因爲模板在別名時沒有實例化,所以static_assert不會觸發。

如果這是可以接受的,您可以添加一些間接方法,並使用構建器元函數來創建您的編譯時間列表。這個元函數會執行檢查。

template <typename ...Ts> 
struct make_list 
{ 
    static_assert(are_integral<Ts...>::value, "all types must be integral"); 
    typedef list<Ts...> type; 
}; 

using ok = make_list<int, long, char>::type; 
using bad = make_list<double>::type; 

另一種解決方案是使用一個虛擬類型的參數包封裝成一流的類型。

// dummy wrapper 
template <typename ...> 
struct pack; 

template <typename ...> struct are_integral; 

// specialization for wrapped packs 
template <typename ...Ts> 
struct are_integral<pack<Ts...>> : are_integral<Ts...> 
{ 
}; 

template <typename T, typename ...Ts> 
struct are_integral<T, Ts...> 
{ 
    static const bool value = std::is_integral<T>::value && 
          are_integral<Ts...>::value; 
}; 

template <> 
struct are_integral<> : std::true_type { }; 

// helper type which performs the check 
template <typename Pack, 
      typename = typename std::enable_if<are_integral<Pack>::value>::type> 
struct list_helper; 

// actual type (alias) you will expose 
template <typename ...Ts> 
using list = list_helper<pack<Ts...>>; 

using ok = list<int, long, char>; 
using bad = list<double>; // compiler error 

使用包裝往往就派上用場了與參數包處理的時候,因爲它使他們更易於操縱:包裝是像任何其他,這can be stored,在參數列表中的任何地方出現一個類型,予以通過到一元函數等。

+0

儘管對於使用列表的人來說,但仍然可以使用'list '作爲模板參數,而沒有編譯器的任何錯誤。我真的希望能夠驗證在我自己的元編程代碼中不會有不變量被破壞,這些代碼操縱這樣的類。 – MadScientist