2013-12-22 2 views
1

Это код, который я хочу писать:VARIADIC рекурсивный шаблон мем весело специализации

template <typename T1, typename ... tail> 
    class record : public record<tail...> 
{ 
    using baseT = record<tail...>; 

    T1 elem; 

public: 
    record(T1 first, tail... rest) 
    : elem(first), baseT(rest...) 
    {} 

    template <int index> 
    inline constexpr T1& get() 
    { 
     // line 83: 
    return baseT::get<index-1>(); 
    } 
    // line 85: 
    template <> 
    inline constexpr T1& get<0>() 
    { 
    return elem; 
    } 
}; 

и вывод компилятора я получаю: (GCC 4.8.2 с -std=c++11)

record.hpp:85:15: error: explicit specialization in non-namespace scope 'class base::record<T1, tail>' 
template <> 
    ^
record.hpp:86:33: error: template-id 'get<0>' in declaration of primary template 
inline constexpr T1& get<0>() 
       ^
record.hpp: In member function 'constexpr T1& base::record<T1, tail>::get() const': 
record.hpp:83:32: error: expected primary-expression before ')' token 
    return baseT::get<index-1>(); 
       ^

Хорошо, что я чтобы достичь ясно: что-то вроде std::tuple. get() предназначен для обеспечения доступа к элементу.

Пожалуйста, помогите мне сработать этот скелет; То, что мне нужно, - это понимание, необходимое для правильной реализации такой конструкции; 2 конкретных вопросов:

  1. Что такое правильный способ специализации get<0>()?
  2. Что будет typename... tail, когда компилятор достигнет корня иерархии?

ответ

5

Если вы хотите явно специализировать функцию (функцию, шаблон функции члена, ..), то вы должны сделать это в области видимости пространства имен:

template <typename T1, typename ... tail> 
    class record : public record<tail...> 
{ 
    using baseT = record<tail...>; 

    T1 elem; 

public: 
    record(T1 first, tail... rest) // you should use perfect forwarding here 
    : elem(first), baseT(rest...) 
    {} 

    template <int index> 
    inline constexpr T1& get() // the `inline` is redundant here 
    { 
     // line 83: 
    return baseT::get<index-1>(); 
    } 
}; 

template<typename T1, typename ... tail> 
template<> 
inline constexpr T1& record<T1, tail...>::template get<0>() 
{ return elem; } 

Но это не допускается: Вы не может явно специфицировать член не явно специализированного шаблона класса. Здесь record<T1, tail...> не является явно специализированным; поэтому вы не можете явно специализировать get.

Есть две другие проблемы:

  1. Тип возврата get должен зависеть от индекса.
  2. Ваш реестр будет рекурсивно выводиться из самого себя. Каждый вывод удаляет один элемент из tail, поэтому tail заканчивается пустым. Тогда record<tail...> не сработает, поскольку первый параметр шаблона T1 не может быть установлен, когда tail пуст. Поэтому вам необходимо также специализироваться на record.

Один из способов заставить его работать, чтобы использовать перегрузки:

#include <type_traits> 
#include <utility> 

template<int N> 
using int_const = std::integral_constant<int, N>; 

template <typename T1, typename ... tail> 
    class record : public record<tail...> 
{ 
    using baseT = record<tail...>; 

    T1 elem; 

protected: 
    using baseT::get_impl; // "unhide" the base class overloads 

    constexpr T1 const& get_impl(int_const<sizeof...(tail)>) const 
    { 
     return elem; 
    } 

public: 
    template<typename T1_, typename ... tail_> 
    record(T1_&& first, tail_&&... rest) 
    : baseT(std::forward<tail_>(rest)...), elem(std::forward<T1_>(first)) 
    {} 

    template <int index> 
    constexpr auto get() const 
    -> decltype(this->get_impl(int_const<sizeof...(tail) - index>{})) 
    { 
    static_assert(1+sizeof...(tail) > index, "out of bounds"); 
    return this->get_impl(int_const<sizeof...(tail) - index>{}); 
    } 
}; 

template <typename T1> 
class record<T1> 
{ 
    T1 elem; 

protected: 
    constexpr T1 const& get_impl(int_const<0>) const 
    { 
     return elem; 
    } 

public: 
    template<typename T1_> 
    record(T1_&& first) 
    : elem(first) 
    {} 

    template <int index> 
    constexpr auto get() const 
    -> decltype(get_impl(int_const<index>{})) 
    { 
    static_assert(0 == index, "out of bounds"); 
    return this->get_impl(int_const<index>{}); 
    } 
}; 


#include <iostream> 

int main() 
{ 
    record<int, double, char, bool> r{42, 1.2, 'c', false}; 
    std::cout << r.get<1>() << '\n'; 
    std::cout << r.get<0>() << '\n'; 
} 

Вот пример, используя другой метод наследования:

#include <type_traits> 
#include <utility> 

template<int N> 
using int_const = std::integral_constant<int, N>; 

template<int N, class... Ts> 
struct record_impl 
{ 
    struct out_of_bounds {}; 

    template<int I> 
    constexpr out_of_bounds get(int_const<I>) const 
    { 
     static_assert(I < N, "out of bounds"); 
     return {}; 
    } 
}; 

template<int N, class T, class... Ts> 
struct record_impl<N, T, Ts...> : record_impl<N+1, Ts...> 
{ 
    using base = record_impl<N+1, Ts...>; 

    T mem; 

    template<class Arg, class... Args> 
    record_impl(Arg&& arg, Args&&... args) 
    : base(std::forward<Args>(args)...), mem(std::forward<Arg>(arg)) 
    {} 

    using base::get; 
    constexpr T const& get(int_const<N>) const 
    { return mem; } 
}; 

template<class... Ts> 
using record = record_impl<0, Ts...>; 


#include <iostream> 

int main() 
{ 
    record<int, double, char, bool> r{42, 1.2, 'c', false}; 
    std::cout << r.get(int_const<0>{}) << '\n'; 
    std::cout << r.get(int_const<3>{}) << '\n'; 
} 

Использование record_impl позволяет получить избавиться от дополнительных get_impl. Он также предоставляет хорошую возможность разместить static_assert в функции члена основного элемента шаблона get.

+0

:/static assert не поможет, так как тип возвращаемого значения не может быть определен в 'decltype', если индекс не соответствует границам. Возможно, вам понадобится сделать более сложную вещь, чтобы вызвать 'static_assert' в случае доступа вне границ. – dyp

+0

Btw: все классы в иерархии будут иметь функцию 'get', но они скрыты высшим классом' get', который имеет правильный 'sizeof ... (tail)'. Этот 'sizeof' необходим, поскольку только верхний класс знает истинную длину хвоста, но элементы в хвосте назначаются в обратном порядке на уровень класса в иерархии. – dyp

+0

Примечание. Вы можете использовать элемент вместо наследования (это также решит обратный порядок и скрытый метод). – Jarod42