2013-05-21 3 views
1

Предположим, у меня есть список параметров шаблона, которые можно увеличить. Я хочу увеличить заголовок этого списка. Вот кодРазрешение перегрузки шаблона Variadic

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

A VARIADIC пакет ласточки * любые аргументы *, '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. Это означает, что я не привязан к работе с одним способом пакетного преобразования аргументов в один тип.

Я также пишу 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 (при этом я не компилировать выше, так что это может быть полна ошибок синтаксиса).

Так вот incrFirst и incrLast после того, как вы перенесли тяжелую к выше мини-библиотека метапрограммирования:

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