2015-06-12 19 views
11

我在C++ 11/14中看到了兩種實現類型列表的可能樣式,我很好奇是否有任何理由相互比較。第一種技術是outlined here,並在Boost的MPL庫上建模。在這種風格中,你定義了元類型的'免費函數'(頂層使用聲明),它們接受類型列表並對它們進行操作。這裏是你將如何實現性病的元版本::變換的類型,而不是在第一樣式值的工作原理:C++ 14類型列表,任何理由更喜歡'自由功能''方法',反之亦然?

template <typename... Args> 
    struct type_list; 

    namespace impl 
    { 
     template <template <typename...> class F, class L> 
     struct transform_impl; 

     template <template <typename...> class F, template <typename...> class L, typename... T> 
     struct transform_impl<F, L<T...>> 
     { 
      using type = L<typename F<T>::type...>; 
     }; 
    } 

    template <template <typename...> class F, class L> 
    using transform = typename impl::transform_impl<F, L>::type; 

第二種風格是定義元「方法」(使用類型列表內聲明結構)。下面是在風格變換容貌:

template <typename... Args> 
    struct type_list { 
     // ... other 'methods' 

     template<template<class> class Wrapper> 
     using transform = 
      type_list<Wrapper<Args>...>; 

     // ... other 'methods' 
    }; 

的優點我在第二個樣式看到的是,你仍然有Args...參數包,所以你不必委託impl輔助功能。兩個可能的缺點是1)你必須把所有的元函數放在type_list裏面,而不是把它們放在單獨的頭文件中,這樣你就失去了一些模塊性,2)'免費'元函數也可以用於元組和其他可變參數模板開箱即用的課程。我不知道在實踐中對#2的渴望實際上有多普遍,我只找到了自己使用type_list和tuple的場合,編寫在meta_list和元組之間轉換的元代碼並不困難。

是否有充分的理由強烈選擇其中一種?也許#2實際上是一個常見的情況?

+7

它是'typename L :: template transform '與'transform '。 –

+0

啊,嗯,沒有考慮調用代碼的醜陋。 –

回答

7

第二個壞是因爲很多原因。

首先,稱它是一團糟。模板內部的模板需要使用template關鍵字。其次,它要求你的類型列表包含你想要在其正文中的類型列表上進行的每個操作。就像將一個string上的每個操作定義爲字符串上的方法一樣:如果允許使用免費函數,則可以創建新操作,甚至可以實現覆蓋。

最後,考慮隱藏::type

開始與這些原語:

template<class T>struct tag{using type=T;}; 
template<class Tag>using type_t=typename Tag::type; 
template<class...Ts>struct types : tag<types<Ts...>>{}; 

變換,或fmap,那麼看起來像:

template<template<class...>class Z, class Types> 
struct fmap; 
template<template<class...>class Z, class...Ts> 
struct fmap<Z, types<Ts...>>:types<Z<Ts...>>{}; 
template<template<class...>class Z, class Types> 
using fmap_t = type_t<fmap<Z,Types>>; 

,你既可以使用type_t<fmap<Z,types<int,double>>>,或fmap_t<Z,types<int,double>>獲取映射類型的類型。

另一種方法是使用含有各種事情constexpr功能:

template<class T>struct tag{using type=T;}; 
template<class...>struct types{using type=types;}; 
template<class Tag>using type_t=typename Tag::type; 

template<template<class...>class Z> 
struct z {template<class...Ts>using apply=Z<Ts...>; constexpr z(){};}; 
template<class...Ts> 
struct one_type {}; 
template<class T0> 
struct one_type<T0> { using type=T0; }; 
template<class...Ts> 
using one_type_t=typename one_type<Ts...>::type; 

template<template<class>class Z> 
struct z_one_base { 
    template<class...Ts> 
    using helper = Z<one_type_t<Ts...>>; 
    using type = z<helper>; 
}; 
template<template<class>class Z> 
using z_one = type_t<z_one_base<Z>>; 

現在fmap很簡單:

// take a template metafunction and a list of types 
// and apply the metafunction to each type, returning the list 
template<template<class...>class Z, class...Ts> 
constexpr auto fmap(z<Z>, types<Ts...>) 
-> types<Z<Ts>...> { return {}; } 

等功能如下:

// a template metafunction and a list of types 
// and apply the template metafunction to all of the types 
template<template<class...>class Z, class...Ts> 
constexpr auto apply(z<Z>, types<Ts...>) 
-> tag<Z<Ts...>> { return {}; } 

// take any number of tags 
// and make a type list from them 
template<class...Tags> 
constexpr auto make_list(Tags...) 
-> types<type_t<Tags>...> { return {}; } 

// concat of nothing is an empty list 
constexpr types<> concat() { return {}; } 
// concat of a list alone is a list alone: 
template<class...T1s> 
constexpr auto concat(types<T1s...>) 
->types<T1s...>{ return {}; } 
// concat of 2 or more lists is the concat of the first two, 
// concatted with the rest 
template<class...T1s, class...T2s, class...Types> 
constexpr auto concat(types<T1s...>,types<T2s...>,Types...) 
->decltype(concat(types<T1s...,T2s...>{},Types{}...)) 
{ return {}; } 


// take a tagged list or a tagged type, and return a list 
template<class T> 
constexpr auto fbox(tag<T>)->types<T> { return {}; } 
template<class...Ts> 
constexpr auto fbox(tag<types<Ts...>>)->types<Ts...> { return {}; } 

// create z_ versions of functions above: 
#define CAT2(A,B) A##B 
#define CAT(A,B) CAT2(A,B) 
// lift functions to metafunctions with z_ prefix: 
#define Z_F(F) \ 
    template<class...Ts> \ 
    using CAT(meta_, F) = decltype(F(Ts{}...)); \ 
    using CAT(CAT(z_, F),_t) = z<CAT(meta_, F)>; \ 
    static constexpr CAT(CAT(z_, F),_t) CAT(z_, F){} 

Z_F(concat); 
//Z_F(apply); 
//Z_F(fmap); 
Z_F(fbox); 
static constexpr z_one<tag> z_tag{}; 


// joins a list of lists or types into a list of types 
template<class...Ts> 
constexpr auto join1(types<Ts...>) 
->type_t<decltype(apply(z_concat, fmap(z_fbox, types<tag<Ts>...>{})))> 
{ return {}; } 
template<class Types> 
constexpr auto join(Types types) 
->type_t<decltype(apply(z_concat, fmap(z_fbox, fmap(z_tag, types))))> 
{ return {}; } 

template<class Z, class...Ts> 
constexpr auto fbind(Z z, Ts...ts) 
->decltype(join(fmap(z, ts...))) 
{ return {}; } 

和工作與僞造類型(tag s)而不是直接在頂層輸入類型。如果您需要在需要時回升至type_t的類型。

我認爲這是一種boost::hana的方法,但我只開始看boost::hana。這樣做的好處是,我們將類型捆綁與操作分離,我們可以訪問完整的C++重載(而不是模板模式匹配,這可能會更脆弱),我們可以直接推導出類型捆綁包的內容,而無需做using和空主要專門技巧。

所有消耗的東西都是包裝類型tag<?>types<?>z<?>,所以沒有什麼是「真實的」。

測試代碼:

template<class T> using to_double = double; 
template<class T> using to_doubles = types<double>; 

int main() { 
    types< int, int, int > three_ints; 

    auto three_double = fmap(z_one<to_double>{}, three_ints); 
    three_double = types<double, double, double >{}; 
    auto three_double2 = join(fmap(z_one<to_doubles>{}, three_ints)); 
    three_double = three_double2; 
    auto three_double3 = fbind(z_one<to_doubles>{}, three_ints); 
    three_double3 = three_double2; 
} 

Live example

+0

我使用第二個,當我想快速煮東西了,這幾乎是:) –

+0

在你的第一個代碼塊的最後一行,我相信你想'types :: tag'不'types:tag' –

+0

@JosephGarvin nope。 'types'從它自己的'tag'繼承。 'tag'的要點是能夠不是一種類型,而是一種談論某種類型的標籤。它可以讓你從'tag​​'繼承,然後有人可以'type_t '來提取另一端的'whatever'。另外,以後,可以將'tag​​'傳遞給'constexpr'函數並進行處理,而實際的'whatever'不能。 – Yakk

相關問題