2013-05-21 100 views
1

假設我有一個可以遞增的模板參數列表。我想增加這個列表的頭部。下面是代碼變量模板重載分辨率

template<int N> 
struct Counter { 
    static constexpr Counter<N+1> increment(); 
}; 
template<int N> 
constexpr Counter<N+1> Counter<N>::increment() { 
    return Counter<N+1>(); 
} 

//List (will be used as List of Counters) 
template <typename... TAIL> 
struct List {}; 

template <typename HEAD, typename... TAIL> 
struct List<HEAD,TAIL...> {}; 



template <typename HEAD, typename... TAIL> 
auto incrFront() -> decltype(List<decltype(HEAD::increment()),TAIL...>()) { 
    return List<decltype(HEAD::increment()),TAIL...>(); 
} 

它確實工作:

auto l0 = incrFront<Counter<0>>(); // Ok, decltype(l0) == List<Counter<1>> 
l0 = List<Counter<1>>(); //Ok, right type 
auto l1 = incrFront<Counter<0>,Counter<1>>(); // Ok, decltype(l1) == List<Counter<1>,Counter<1>> 
l1 = List<Counter<1>,Counter<1>>(); 

現在,我想遞增名單的後面,所以

template <typename... HEAD, typename TAIL> 
auto incrBack() -> decltype(List<decltype(HEAD...,TAIL::increment())>()) { 
    return List<decltype(HEAD...,TAIL::increment()>(); 
} 

但有錯誤 'incrBack'未在此範圍內聲明

我試圖在此之前添加另一種方法:

template <typename... HEAD> 
auto incrBack() -> decltype(List<HEAD...>()) { 
    std::cout << "Should not be here\n"; 
    return List<HEAD...>(); 
} 

希望這個方法在解析重載時永遠不會被調用,但是這個方法的確被調用。

任何線索?我只是想這個例子是工作:

auto l2 = incrBack<Counter<1>,Counter<1>>(); // I want decltype(l2) == List<Counter<1>,Counter<2>> 
l2 = incrFront<Counter<0>,Counter<2>>(); //should be possible 
+3

一個可變參數包燕子*任何及所有參數*'TAIL'將永遠不會有一個類型, 'incrBack'從重載解析中移除。 – Xeo

+0

你需要遞歸來消化所有的參數,直到你到達最後並遞增它。 – jrok

+0

這真的是你想要'incrFront'工作嗎?因爲沒有任何推導,並且列表沒有被封裝,所以每次都必須使用明確的參數包來調用它。有點失敗的目的,不是? – Potatoswatter

回答

3

首先,編寫代碼,上List<>,而不是赤裸裸的variardic類型列表中運行,它會更容易。

其次,不是使用模板函數,而是使用模板類。

template<typename T> 
struct inc { 
    typedef decltype(T::increment()) type; 
}; 
template<typename T> 
using Inc = typename inc<T>::type; 

現在我們可以談論Inc<HEAD>代替decltype(HEAD::increment()),這應該使你的代碼更具可讀性。

編寫如下template類:

template<typename List, typename T> 
struct append; 
template<typename List, typename T> 
using Append = typename append<List,T>::type; 
template<template<typename...>class TypeList, typename... Ts, typename T> 
struct append<TypeList<Ts...>, T> { 
    typedef TypeList<Ts..., T> type; 
}; 
template<typename List> 
struct reverse; 
template<typename List> 
using Reverse = typename reverse<List>::type; 
template<template<typename...>class TypeList> 
struct reverse<TypeList<>> { 
    typedef TypeList<> type; 
}; 
template<template<typename...>class TypeList, typename T0, typename... Ts> 
struct reverse<TypeList<T0, Ts...>> { 
    typedef Append< Reverse<TypeList<Ts...>>, T0 > type; 
}; 

以及類似的。請注意,我使用類型進行操作,並專注於從傳遞到template的某些通用variardic類型包生成的類型 - 這意味着我並不想用一種方法將variardic參數包裝爲單一類型。

我還寫了template別名以減少typename垃圾郵件。

接下來,應用函子:

template<template<typename>class Func, typename List> 
struct apply_to_first; 
template<template<typename>class Func, typename List> 
using ApplyToFirst = typename apply_to_first<Func, List>::type; 
template<template<typename>class Func, template<typename...>class TypeList, typename T0, typename... Ts> 
struct apply_to_first<Func, TypeList<T0, Ts...>> { 
    typedef TypeList< typename Func<T0>::type, Ts... > type; 
}; 

然後IncFirst

template<typename List> 
using IncFirst = ApplyToFirst< inc, List >; 

這是目前很短。

至於IncLast,這是稍微難:

template<typename List> 
using IncLast = Reverse< IncFirst < Reverse<List> > >; 

,但仍符合上一條線。不過,我更喜歡這種更詳細的版本:

template<template<typename>class Func, typename List> 
using ApplyToLast = Reverse< ApplyToFirst< Func, Reverse<List> > >; 

template<typename List> 
using IncLast = ApplyToLast< inc, List >; 

現在,我沒有直接回答你的問題,就是我從來沒有寫過incrFront(我也沒有編譯上面,所以它可能是充滿語法錯誤的)。

因此,這裏是incrFirstincrLast您已經移動繁重上述微型元編程庫後:

template<typename... Ts> 
IncFirst<List<Ts...>> incrFirst() { 
    return IncFirst<List<Ts...>>(); 
} 
template<typename... Ts> 
IncLast<List<Ts...>> incrLast() { 
    return IncLast<List<Ts...>>(); 
} 
+0

很好,非常感謝,我很難理解我對這類事物的新看法,但我發現這裏的關鍵點是「反向」,我也想過這樣的事情,但是,無法實現它你有任何參考或網絡教程這種TMP與C + + 11(「現代C + +設計」似乎不錯,但不是最新的...) –

+1

@BérengerBerthoul不是我大部分這些技術都是相對較新的,因爲它們在C++ 11之前並不存在。學習像LISP或Haskell這樣的語言可以提供很大的幫助,因爲C++ 11 TMP是一種函數式編程語言,以及用於成熟函數式編程語言的技術可以爲C++ 11 TMP等相對年輕的語言提供信息。 – Yakk