class instantiation in nested classes - c++

I have the following problem:
template< typename T, size_t N, size_t... N_i >
struct A
{
// nested class
template< typename... Ts >
class B
{
//...
A<T, N_i...>::B< Ts... > operator[]( size_t i )
{
A< T, N_i...>::B< Ts... > res{ /* ... */ };
return res;
}
// ...
};
};
Unfortunately, the compiler yields an error for "A < T, N_i...>::B < Ts... > res{ /* ... */ };". Does anyone know how I can return an instantiation of the class B (which differs in its template arguments of his outer class A) in the function "operator[]" of class B?
Many thanks in advance.

Should be enough add a typename before A<T, N_i...> and a template before B< Ts... >.
I mean
template< typename T, size_t N, size_t... N_i >
struct A
{
// nested class
template< typename... Ts >
class B
{
//...
typename A<T, N_i...>::template B< Ts... > operator[]( size_t i )
{
typename A< T, N_i...>::template B< Ts... > res{ /* ... */ };
return res;
}
// ...
};
}
P.s.: should work also with C++11; not only with C++14

Putting a typename before the full type of the return types is what fixes the errors you're getting. However, don't put the template parameters after the B as B always refers to the "current" instantiation of the type.
#include <stdio.h>
using namespace std;
template< typename T, size_t N, size_t... N_i >
struct A
{
// nested class
template< typename... Ts >
class B
{
//...
typename A<T, N_i...>::B operator[]( size_t i )
{
typename A< T, N_i...>::B res{ /* ... */ };
return res;
}
// ...
};
};
https://godbolt.org/g/hsB4pW

Related

Variadic template compilation issue on MS Visual Studio 2017

I need a template to find out the order of types in which the class inherits from its bases and their index. The code works fine with clang and gcc but in Visual Studio, which is the target environment, I'm getting an internal compiler error "fatal error C1001: An internal error has occurred in the compiler.". I'm looking for some workaround or maybe an error in my code. Yes, I have already tried google.
Thanks, in advance.
#include <type_traits>
#include <iostream>
struct BaseA
{
};
struct BaseB
{
};
struct BaseC
{
};
template <class... Types>
class type_list {};
template<typename Type, typename TypeList>
struct get_idx_for_type;
template<typename Type, template<typename...> typename TypeList, typename ...Types>
struct get_idx_for_type<Type, TypeList<Types...>>
{
template<int I, typename T, typename ...Rest>
struct find_type;
template<int I, typename T, typename U, typename ...Rest>
struct find_type< I, T, U, Rest... >
{
// problematic line for compiler, problem is somewhere in find_type recursion
static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, Rest...>::value;
};
template<int I, typename T, typename U>
struct find_type< I, T, U >
{
static constexpr int value = std::is_same<T, U>::value ? I : -1;
};
static constexpr int value = find_type<0, Type, Types...>::value;
};
template<typename ...Bases>
struct Foo : public Bases...
{
using base_types_list = type_list<Bases...>;
};
int main()
{
using T = Foo<BaseA, BaseB, BaseC>;
Foo<BaseA, BaseB, BaseC> q;
int a = get_idx_for_type<BaseA, T::base_types_list>::value;
std::cout << a << std::endl;
return 0;
}
An internal compiler error is always a bug in the compiler, whether or not there
is anything wrong with your code. To work around this one, you can replace:
template<int I, typename T, typename U, typename ...Rest>
struct find_type< I, T, U, Rest... >
{
// problematic line for compiler, problem is somewhere in find_type recursion
static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, Rest...>::value;
};
with:
template<int I, typename T, typename U, typename V, typename ...Rest>
struct find_type< I, T, U, V, Rest... >
{
static constexpr int value = std::is_same<T, U>::value ? I : find_type<I + 1, T, V, Rest...>::value;
};
to assist VC++ in disambiguating it from:
template<int I, typename T, typename U>
struct find_type< I, T, U >
{
static constexpr int value = std::is_same<T, U>::value ? I : -1;
};
when ...Rest is empty.
Live demo
I'm getting an internal compiler error "fatal error C1001: An internal error has occurred in the compiler.".
So, I suppose, your code is correct but the compiler is bugged.
I'm looking for some workaround or maybe an error in my code.
I don't have your compiler, so it's a shoot in the dark, but I propose to rewrite your find_type struct using another specialization (a specialization for U and T are the same type; a specialization for U and T are different) and inherit the value from std::integral_constant.
So I propose the following
template <int, typename ...>
struct find_type;
template <int I, typename T>
struct find_type<I, T> : public std::integral_constant<int, -1>
{ };
template <int I, typename T, typename ... Rest>
struct find_type<I, T, T, Rest...> : public std::integral_constant<int, I>
{ };
template <int I, typename T, typename U, typename ... Rest>
struct find_type<I, T, U, Rest...> : public find_type<I+1, T, Rest...>
{ };
Another possible alternative is pass through a template function, instead of a find_type struct.
By example... if you define a static constexpr method find_type_func() as follows (C++17, but I can easily adapt it to C++14 if you want)
template <typename T, typename ... List>
constexpr static int find_type_func ()
{
int ret { -1 };
int i { 0 };
((ret = (ret == -1) && std::is_same<T, List>{} ? i : ret, ++i), ...);
return ret;
}
you can delete the struct find_type and initialize value as follows
static constexpr int value = find_type_func<Type, Types...>();
-- EDIT --
As asked, a C++14 version
template <typename T, typename ... List>
constexpr static int find_type_func ()
{
using unused = int[];
int ret { -1 };
int i { 0 };
(void)unused { 0, (std::is_same<T, List>::value ? ret = i : ++i)... };
return ret;
}

member function implementation depending on template parameter

I have the following problem:
template< typename T, size_t N, size_t... N_i >
class A
{
public:
// ...
// first implementation
template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M!=1, size_t >::type = 0 >
A<T, N_i...> operator[]( size_t i )
{
A< T, N_i... > res{ ... };
return res;
}
// second implementation
template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M==1, size_t >::type = 0 >
T operator[]( size_t i )
{
return ... ;
}
};
As you can see above, I try to implement a class A which expects as template arguments a type T (e.g. int or float) and sizeof...(N_i)+1-many size_t.
Dependent on the number of passed size_t (i.e. sizeof...(N_i)+1), I will use a different implementation for the member function operator[](size_t) with a different result type:
one implementation for the case sizeof...(N_i)+1 > 1 with return type A < T, N_i... >
(referred to as "first implementation" in the code)
and one for the case sizeof...(N_i)+1 == 1 with return type T
(referred to as "second implementation" in the code).
Unfortunately, I have no idea how this can be implemented -- the solution above does not work. Has anyone an idea?
Many thanks in advance.
A<T, N_i...> is invalid for empty N_i.
As workaround, you may use indirection:
template <typename, std::size_t ...>
struct PopFrontA
{
using type = void; // Dummy type for A<T, N>
};
template< typename T, std::size_t N, std::size_t... N_i > class A;
template <typename T, std::size_t N, std::size_t N2, std::size_t ... Ns>
struct PopFrontA<T, N, N2, Ns...>
{
using type = A<T, N2, Ns...>;
};
template <typename T, std::size_t ... Ns>
using PopFrontA_t = typename PopFrontA<T, Ns...>::type;
And then
// first implementation
template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M!=1, size_t >::type = 0 >
PopFrontA_t<T, N, N_i...>
operator[]( size_t i )
{
A< T, N_i... > res{ /*...*/ };
return res;
}
Demo
If you modify
A< T, N_i... > res{ ... };
in
A< T, N_i... > res{ };
and
return ... ;
in
return T{} ;
isn't enough?
--- EDIT ---
No: as pointed by Jarod42 (thanks!), isn't enough.
So I propose the following solution, based on class template specialization and std::conditional to avoid the use of SFINAE
#include <iostream>
#include <type_traits>
template< typename, size_t...>
class A;
template< typename T, size_t N, size_t... N_i >
class A<T, N, N_i...>
{
public:
template <typename Next = typename std::conditional<sizeof...(N_i),
A<T, N_i...>, T>::type>
Next operator[] (size_t i)
{ return Next{}; }
};
int main(int argc, char* argv[])
{
A<int, 2, 4> a;
std::cout << a[1][2] << std::endl;
return 0;
}
If you don't want specialize A, you can add a sub-struct of A to do the dirty work.
#include <iostream>
#include <type_traits>
template< typename T, size_t N, size_t... N_i >
class A
{
template <typename U, size_t ... O_i>
struct Next
{ using type = A<U, O_i...>; };
template <typename U>
struct Next<U>
{ using type = U; };
public:
using next_t = typename Next<T, N_i...>::type;
next_t operator[] (size_t i)
{ return next_t{}; }
};
int main(int argc, char* argv[])
{
A<int, 2, 4> a;
std::cout << a[1][2] << std::endl;
return 0;
}

Extract template class default parameters

Is there a way to extract a template class' default parameters only knowing the unspecialized template class at compile time?
I know how to extract an instantiated template class' parameters, like this:
// Just an example class for the demonstration
template<class A, class B=void>
struct example {};
// Template parameters storage class
template<class...An>
struct args;
// MPL utility that extracts the template arguments from a template class
template<class C>
struct get_args
{
typedef args<> type;
};
template<template<class...> class C, class...An>
struct get_args< C<An...> >
{
typedef args<An...> type;
};
// And the assertion
static_assert(
std::is_same
< args<int,void>,
get_args< example<int> >::type
>::value,
"Check this out"
);
Now what I would like to know is if decltype or anything else could be used to retrieve the default template parameters only from the unspecialized template class:
// MPL utility that extract template default arguments from a template class
template<template<class...> class C>
struct get_default_args
{
typedef /* what goes here? */ type;
};
// And the assertion
static_assert(
std::is_same
< args<void>,
get_default_args< example >::type
>::value,
"Check this out"
);
For now, I only figured out how to extract the number of parameters of a template class, not their default value:
namespace detail {
template< template<class> class >
boost::mpl::size_t<1> deduce_nb_args();
template< template<class,class> class >
boost::mpl::size_t<2> deduce_nb_args();
template< template<class,class,class> class >
boost::mpl::size_t<3> deduce_nb_args();
/* ... and so on ... */
}
// MPL utility that extract the number of template arguments of a template class
template<template<class...> class C>
struct get_nb_args :
decltype(detail::deduce_nb_args<C>()) {};
// And the assertion
static_assert(
get_nb_args< example >::value == 2,
"Check this out"
);
Edit
It seems that at the end, and once again, MSVC prevents me to perform this operation.
Something like the following makes the compiler crash with a fatal error C1001: An internal error has occurred in the compiler.
template<template<class...> class D> static
boost::boost::mpl::int_<0> mandatory(D<>*)
{ return boost::boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<1> mandatory(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class...> class D> static
boost::mpl::int_<2> mandatory(D<void,void>*)
{ return boost::mpl::int_<0>(); }
template<template<typename...> class D> static
boost::mpl::int_<-1> mandatory(...)
{ return boost::mpl::int_<-1>(); }
int check()
{
return mandatory<example>(nullptr);
}
Trying next one results in error C2976: 'D' : too few template arguments
template<template<class,class> class D> static
boost::mpl::int_<0> mandatory2(D<>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<1> mandatory2(D<void>*)
{ return boost::mpl::int_<0>(); }
template<template<class,class> class D> static
boost::mpl::int_<2> mandatory2(D<void,void>*)
{ return boost::mpl::int_<0>(); }
int check2()
{
return mandatory2<example>(nullptr);
}
So to me it seems that no matter the approach, MSVC forbids programmatic instantiation of a template class making use of default parameters.
In turn, it looks impossible to me to use a SFINAE technique to extract:
1. the mandatory number of parameters;
2. the types of default parameters.
Edit 2
Ok, after several tests it seems to be a bug with MSVC occurring when trying to programmatically instantiate a template class only using default arguments.
I filed a bug report here and another one here.
Here is a traits class allowing to check if a class is instantiable using given template parameters that does not make the compiler crash, but do not evaluates to true for fully default instantiable classes.
namespace detail {
typedef std::true_type true_;
typedef std::false_type false_;
template< template<class...> class D, class...An >
true_ instantiable_test(D<An...>*);
template< template<class...> class D, class...An >
false_ instantiable_test(...);
}
template< template<class...> class C, class...An >
struct is_instantiable : decltype(detail::instantiable_test<C,An...>(nullptr)) {};
That being said, it seems impossible with MSVC to retrieve the template type instantiated with default parameters. Typically the following does not compile:
template< template<class...> class T, class...An >
struct get_default_v0
{
typedef T<An...> type;
};
namespace detail {
template< template<class...> class T, class...An >
T<An...> try_instantiate();
} // namespace detail
template< template<class...> class T, class...An >
struct get_default_v1
{
typedef decltype(detail::try_instantiate<T,An...>()) type;
};
// C2976
static_assert(
std::is_same< get_default_v0<example,int> , example<int,void> >::value,
"v0"
);
// C2976
static_assert(
std::is_same< get_default_v1<example,int> , example<int,void> >::value,
"v1"
);
I'd try something like this:
template <typename ...> struct Get2;
template <template <typename...> class Tmpl,
typename A, typename B, typename ...Rest>
struct Get2<Tmpl<A, B, Rest...>>
{
using type = B;
};
template <template <typename...> class Tmpl> struct GetDefault2
{
using type = typename Get2<Tmpl<void>>::type;
};
I realize this is a long answer, but here is a possible approach:
#include <type_traits>
namespace tmpl
{
namespace detail
{
template<template<typename...> class C, typename... T>
struct is_valid_specialization_impl
{
template<template<typename...> class D>
static std::true_type test(D<T...>*);
template<template<typename...> class D>
static std::false_type test(...);
using type = decltype(test<C>(0));
};
} // namespace detail
template<template<typename...> class C, typename... T>
using is_valid_specialization = typename detail::is_valid_specialization_impl<C, T...>::type;
} // namespace tmpl
The following is a partial copy/paste from my github repository, dont worry too much about it, most of the code is to find the minimum/maximum number of template arguments required (in this case we only care about the minimum number):
#if !defined(TEMPLATE_ARGS_MAX_RECURSION)
#define TEMPLATE_ARGS_MAX_RECURSION 30
#endif
namespace tmpl
{
namespace detail
{
enum class specialization_state {
invalid,
valid,
invalid_again
};
template<bool, template<typename...> class C, typename... T>
struct num_arguments_min
: std::integral_constant<int, sizeof...(T)>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_min<false, C, T...>
: num_arguments_min<is_valid_specialization<C, T..., char>::value, C, T..., char>
{ };
template<specialization_state, template<typename...> class C, typename... T>
struct num_arguments_max;
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid, C, T...>
: num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid,
C,
T..., char
>
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::valid, C, T...>
: std::conditional<
((sizeof...(T) == 0) || (sizeof...(T) == TEMPLATE_ARGS_MAX_RECURSION)),
std::integral_constant<int, -1>,
num_arguments_max<
is_valid_specialization<C, T..., char>::value
? specialization_state::valid
: specialization_state::invalid_again,
C,
T..., char
>
>::type
{ };
template<template<typename...> class C, typename... T>
struct num_arguments_max<specialization_state::invalid_again, C, T...>
: std::integral_constant<int, (sizeof...(T) - 1)>
{ };
} // namespace detail
template<template<typename...> class C>
struct template_traits
{
constexpr static int args_min = detail::num_arguments_min<is_valid_specialization<C>::value, C>::value;
constexpr static int args_max = detail::num_arguments_max<is_valid_specialization<C>::value
? detail::specialization_state::valid
: detail::specialization_state::invalid,
C>::value;
constexpr static bool is_variadic = (args_max < args_min);
template<typename... T>
using specializable_with = is_valid_specialization<C, T...>;
};
} // namespace tmpl
Some helper types specifically for your question:
template<typename... Ts>
struct type_sequence { };
namespace detail
{
template<int N, typename...>
struct skip_n_types;
template<int N, typename H, typename... Tail>
struct skip_n_types<N, H, Tail...>
: skip_n_types<(N - 1), Tail...> { };
template<typename H, typename... Tail>
struct skip_n_types<0, H, Tail...>
{
using type = type_sequence<H, Tail...>;
};
} // namespace detail
template<int N, typename... T>
using skip_n_types = typename detail::skip_n_types<N, T...>::type;
namespace detail
{
template<typename T>
struct get_default_args;
template<template<typename...> class T, typename... A>
struct get_default_args<T<A...> >
{
using type = typename skip_n_types<
tmpl::template_traits<T>::args_min,
A...>::type;
};
} // namespace detail
template<typename T>
using get_default_args = typename detail::get_default_args<T>::type;
To put it all together:
template<typename>
struct dependant { };
template<typename T, typename U = void>
struct example { };
template<typename T, typename U = dependant<T> >
struct dependant_example { };
template<typename T>
void print_type(T)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main(int argc, char** argv)
{
{
using example_type = example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
{
using example_type = dependant_example<int>;
using default_args = get_default_args<example_type>;
print_type(example_type{});
print_type(default_args{});
}
}
Output:
void print_type(T) [T = example<int, void>]
void print_type(T) [T = type_sequence<void>]
void print_type(T) [T = dependant_example<int, dependant<int> >]
void print_type(T) [T = type_sequence<dependant<int> >]

How to remove last argument of variadic template

I have following structure, I want remove last argument from index_sequence :
template< std::size_t ... values>
struct index_sequence{};
// I need something like
template< typename IndexSequence>
struct pop_back;
template< std::size_t ... values >
struct pop_back< index_sequence< values... > >
{
typedef index_sequence< /** values except last one*/ > type;
};
How to implement this pop_back structure?
I know implementation, only it requires deep recursion, I want without deep recursion instantination.
My implementation:
template< std::size_t i, typename IndexSequence >
struct insert_head;
template< std::size_t i, std::size_t ...values>
struct insert_head< i, index_sequence<values...> >
{
typedef index_sequence< i, values... > type;
};
template< >
struct pop_back< index_sequence<> >
{
typedef index_sequence<> type; // no element will removed
};
template< std::size i > struct pop_back< index_sequence< i > >
{
typedef index_sequence<> type; // i - will removed
};
template< std::size_t i, std::size_t ...values>
struct pop_back< index_sequence<i,values...>>
{
typedef typename pop_back< index_sequence<values...> >::type tail;
typedef typename insert_head< i, tail>::type type;
};
Edit2: Another useful algo, select i-th element !!!
template< std::size ...i> struct index_sequence;
template< std::size_t index, typename IndexSeq> struct at;
template< std::size_t index, std::size_t ... values>
struct at< index, index_sequence<values...> >
{
static constexpr std::size_t get_value()noexcept
{
using list = std::size_t [];
return list{ values...}[index];
}
static constexpr std::size_t value = get_value();
}
// test
// -- 0 1 2 3 4
typedef index_sequence<2,4,6,8,10> even_t;
static_assert( at<2, even_t>::value == 6, "!");
Again, using Xeo's O(logN) instantiation depth version of gen_seq, slightly modified:
#include <cstddef>
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<std::size_t...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<std::size_t... I1, std::size_t... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<std::size_t N> struct gen_seq;
template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;
template<std::size_t N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Now, using one of my former tricks via a friend function & ADL:
#include <tuple>
#include <type_traits>
template<class T, std::size_t I>
struct type_index_pair
{
friend T my_declval(type_index_pair,
std::integral_constant<std::size_t, I>);
};
template<class, class>
struct pop_back_helper;
template<class... TT, std::size_t... Is>
struct pop_back_helper<std::tuple<TT...>, seq<Is...>>
{
struct base : type_index_pair<TT, Is>...
{};
template<std::size_t... Is2>
using join = std::tuple< decltype(my_declval(base{},
std::integral_constant<std::size_t, Is2>{}))... >;
};
template<class... TT, std::size_t... Is, std::size_t... Is2>
auto deduce(seq<Is...>, seq<Is2...>)
-> typename pop_back_helper<std::tuple<TT...>, seq<Is...>>
::template join<Is2...>
{ return {}; } // definition not required, actually
template<class... TT>
using pop_back = decltype(deduce<TT...>(gen_seq<sizeof...(TT)>{},
gen_seq<sizeof...(TT)-1>{}));
Usage example:
#include <iostream>
template<class T>
void pretty_print(T)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
pretty_print( pop_back<int, bool, char, double>{} );
pretty_print( pop_back<double, int, int>{} );
}
I'm not particularly happy with it, as it requires two sequences plus the ADL (which requires resources and is slow, AFAIK). Maybe I'll be able to come up with something better in one of next days.
there is solution my own question:
template< int ...i> struct seq{};
// GCC couldn't optimize sizeof..(i) ,
//see http://stackoverflow.com/questions/19783205/why-sizeof-t-so-slow-implement-c14-make-index-sequence-without-sizeof
//so I use direct variable `s` instead of it.
// i.e. s == number of variadic arguments in `I`.
template< int s, typename I, typename J > struct concate;
template< int s, int ...i, int ...j>
struct concate<s, seq<i...>, seq<j...> >
{
typedef seq<i..., (s + j)...> type;
};
template<int n> struct make_seq_impl;
template< int n> using make_seq = typename make_seq_impl<n>::type;
template<> struct make_seq_impl<0>{ typedef seq<> type;};
template<> struct make_seq_impl<1>{ typedef seq<0> type;};
template<int n> struct make_seq_impl: concate< n/2, make_seq<n/2>, make_seq<n-n/2>>{};
//----------------------------------------------------
// Our solution:
template< std::size_t ...> struct index_sequence{};
template< typename IndexSequence> struct pop_back;
// empty index_sequence
template<>struct pop_back< index_sequence<> >
{
typedef index_sequence<> type;
};
template< std::size_t ...i>
struct pop_back< index_sequence<i...> >
{
static constexpr std::size_t size = sizeof...(i);
static constexpr std::size_t values[] = {i...};
template< typename sq> struct apply;
template< int ...j> struct apply< seq<j...> >
{
typedef index_sequence< values[j]... > type;
};
typedef typename apply< make_seq< size - 1 > >::type type;
};
// test
int main()
{
typedef index_sequence< 2, 4, 6, 8, 10> ievens;
typedef pop_back< ievens>::type jevens;
static_assert( std::is_same< jevens, index_sequence<2,4,6,8> >::value ,"!");
}

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

I have problem with implementing recursive template (function in template struct), which will be terminated by std::tuple_size.
Here is fragment of code (I simplified code, to emphasize problem):
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);
}
Initial template call looks like this:
Helper<0, T_arguments...>::func (return_size, arguments);
GCC fails with error:
error: template argument ‘std::tuple_size::value’
involves template parameter(s)
struct Helper::value, T_arguments...>
std::tuple_size is claimed to be known at compile time, so why I cannot use it template specialization?
Actually what you're doing is forbidden by section §14.5.4/9 which says,
A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.
Following may help:
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);
}
Use index_sequence and range-based-for.
#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("%d\n", result);
}