递归可变参数模板在 C++11 中由 std::tuple_size 终止

Recursive variadic template terminated by std::tuple_size in C++11

本文关键字:std tuple 终止 size 中由 C++11 变参 参数 递归      更新时间:2023-10-16

我在实现递归模板(模板结构中的函数)时遇到问题,它将由 std::tuple_size 终止。

这是代码片段(我简化了代码,以强调问题):

template<int index, typename ...T_arguments>
    struct Helper
    {
        static void func (size_t&                           return_size,
                          const std::tuple<T_arguments...>& arguments)
        {
            const auto& argument (std::get<index> (arguments));
            return_size += ::value_size (argument);
            ::Helper<index + 1, T_arguments...>::func (return_size, arguments);
        }
// ...
template<typename... T_arguments>
    struct Helper<std::tuple_size<T_arguments...>::value, T_arguments...>
    {
        static void func (size_t&                           return_size,
                          const std::tuple<T_arguments...>& arguments)
        {
            const auto& argument (std::get<std::tuple_size<T_arguments...>::value> (arguments));
            return_size += ::value_size (argument);
        }

初始模板调用如下所示:

Helper<0, T_arguments...>::func (return_size, arguments);

GCC 失败并显示错误:

错误:模板参数"std::tuple_size::值" 涉及模板参数 结构 帮助程序::值, T_arguments...>

std::tuple_size声称在编译时是已知的,那么为什么我不能使用它模板专用化呢?

实际上,

您正在做的事情是第 §14.5.4/9 节禁止的,该节规定:

部分专用

非类型参数表达式不得涉及部分专用化的模板参数,除非参数表达式是简单标识符。

以下可能会有所帮助:

template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
  total_value_size(size_t& return_size, const std::tuple<Tp...>& t)
  { }
template<std::size_t I = 0, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
  total_value_size(size_t& return_size, const std::tuple<Tp...>& t)
  {
        const auto& argument (std::get<I> (t));
        return_size += ::value_size(argument);
        total_value_size<I + 1, Tp...>(return_size, t);
  }

使用 index_sequence 和基于范围。

#include <cstdlib>
#include <cstddef>
#include <tuple>
namespace mpl
{
    template< std::size_t ... I>
    struct index_sequence
    {
    };
    template< std::size_t s, typename I1, typename I2>
    struct concate;
    template< std::size_t s, std::size_t ...I, std::size_t ...J>
    struct concate<s, index_sequence<I...>, index_sequence<J...> >
    {
        typedef index_sequence<I... ,( J + s)... > type;
    };

    template< std::size_t N>
    struct make_index_sequence
    {
        typedef typename concate< N/2,
           typename make_index_sequence< N/2>::type,
           typename make_index_sequence< N - N/2>::type
        >::type type;
    };
    template<>struct make_index_sequence<0>
    {
        typedef index_sequence<> type;
    };
    template<> struct make_index_sequence<1>
    {
        typedef index_sequence<0> type;
    };
    template< typename ...T>
    struct index_sequence_for
    {
        typedef typename make_index_sequence< sizeof...(T) > ::type type;
    };
} // mpl

template< typename T >
std::size_t value_size( T ){ return sizeof(T); }// only for illustration
template< typename ...Tp, std::size_t ...i>
std::size_t total_value_size_impl( const std::tuple<Tp...> & t, mpl::index_sequence<i...> )
{
    std::size_t result=0;
    for(auto x: { value_size( std::get<i>(t) ) ... } )
    {
          result += x;
    }
    return result;
}
template< typename ...Tp>
std::size_t total_value_size( const std::tuple<Tp...> & t)
{
    typedef typename mpl::index_sequence_for<Tp...> :: type indexes;
    return total_value_size_impl( t, indexes{} );
}
#include <cstdio>
int main()
{
    typedef std::tuple<int, char, double> types;
    std::size_t result = total_value_size(types{});
    printf("%dn", result);
}