Можно ли "сохранить" пакет параметров шаблона, не расширяя его?
Я экспериментировал с вариабельными шаблонами C++0x, когда наткнулся на эту проблему:
template < typename ...Args >
struct identities
{
typedef Args type; //compile error: "parameter packs not expanded with '...'
};
//The following code just shows an example of potential use, but has no relation
//with what I am actually trying to achieve.
template < typename T >
struct convert_in_tuple
{
typedef std::tuple< typename T::type... > type;
};
typedef convert_in_tuple< identities< int, float > >::type int_float_tuple;
GCC 4.5.0 выдает ошибку при попытке ввести определение пакета параметров шаблона.
По сути, я хотел бы "сохранить" пакет параметров в typedef, не распаковывая его. Является ли это возможным? Если нет, есть ли какая-то причина, почему это не разрешено?
4 ответа
Другой подход, который немного более общий, чем у Бена, заключается в следующем:
#include <tuple>
template <typename... Args>
struct variadic_typedef
{
// this single type represents a collection of types,
// as the template arguments it took to define it
};
template <typename... Args>
struct convert_in_tuple
{
// base case, nothing special,
// just use the arguments directly
// however they need to be used
typedef std::tuple<Args...> type;
};
template <typename... Args>
struct convert_in_tuple<variadic_typedef<Args...>>
{
// expand the variadic_typedef back into
// its arguments, via specialization
// (doesn't rely on functionality to be provided
// by the variadic_typedef struct itself, generic)
typedef typename convert_in_tuple<Args...>::type type;
};
typedef variadic_typedef<int, float> myTypes;
typedef convert_in_tuple<myTypes>::type int_float_tuple;
int main()
{}
Я думаю, что причина, по которой это запрещено, в том, что это будет грязно, и вы можете обойти это. Вам необходимо использовать инверсию зависимостей и сделать структуру, хранящую пакет параметров, в фабричный шаблон способной применить этот пакет параметров к другому шаблону.
Что-то вроде:
template < typename ...Args >
struct identities
{
template < template<typename ...> class T >
struct apply
{
typedef T<Args...> type;
};
};
template < template<template<typename ...> class> class T >
struct convert_in_tuple
{
typedef typename T<std::tuple>::type type;
};
typedef convert_in_tuple< identities< int, float >::apply >::type int_float_tuple;
Я нашел идею Бена Фойгта очень полезной в моих собственных начинаниях. Я немного изменил его, чтобы сделать его общим, а не просто кортежами. Для читателей здесь это может быть очевидной модификацией, но, возможно, стоит показать:
template <template <class ... Args> class T, class ... Args>
struct TypeWithList
{
typedef T<Args...> type;
};
template <template <class ... Args> class T, class ... Args>
struct TypeWithList<T, VariadicTypedef<Args...>>
{
typedef typename TypeWithList<T, Args...>::type type;
};
Имя TypeWithList проистекает из того факта, что тип теперь создается с помощью предыдущего списка.
Это разновидность хитрого метода частичной специализации GManNickG. Без делегирования, и вы получите больше безопасности типов, требуя использования вашей структуры variadic_typedef.
#include <tuple>
template<typename... Args>
struct variadic_typedef {};
template<typename... Args>
struct convert_in_tuple {
//Leaving this empty will cause the compiler
//to complain if you try to access a "type" member.
//You may also be able to do something like:
//static_assert(std::is_same<>::value, "blah")
//if you know something about the types.
};
template<typename... Args>
struct convert_in_tuple< variadic_typedef<Args...> > {
//use Args normally
typedef std::tuple<Args...> type;
};
typedef variadic_typedef<int, float> myTypes;
typedef convert_in_tuple<myTypes>::type int_float_tuple; //compiles
//typedef convert_in_tuple<int, float>::type int_float_tuple; //doesn't compile
int main() {}