Variadic递归模板mem有趣的专业化

Hai*_*x64 1 c++ specialization variadic-templates c++11

这是我想写的代码:

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;
  }
};
Run Code Online (Sandbox Code Playgroud)

和我得到的编译器输出:( GCC 4.8.2 with -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>();
                ^
Run Code Online (Sandbox Code Playgroud)

好吧,我的意思是明确的:有点像std::tuple.get()旨在提供元素访问.

请帮助我让这个骨架工作; 我所追求的是正确实现这种结构所需的理解; 但有两个具体问题:

  1. 什么是专业化的正确方法get<0>()
  2. typename... tail当编译器到达层次结构的根时,会是什么?

dyp*_*dyp 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;  }
Run Code Online (Sandbox Code Playgroud)

但是这是不允许的:您可能没有明确地专门化一个非显式专用类模板的成员.在这里,record<T1, tail...>没有明确专业化; 因此你可能没有明确专门化get.

还有另外两个问题:

  1. 返回类型get必须取决于索引.
  2. 您的记录将以递归方式从自身派生.每个派生将从中删除一个元素tail,因此tail最终为空.然后,record<tail...>将失败,因为第一个模板参数T1tail空时无法设置.因此,您也需要专业化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';
}
Run Code Online (Sandbox Code Playgroud)

这是使用不同继承技术的示例:

#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';
}
Run Code Online (Sandbox Code Playgroud)

使用record_impl允许摆脱额外的get_impl.它还提供了一个static_assert在主模板的get成员函数中放置的好机会.