Можно ли сохранить пакет параметров шаблона, не расширяя его?

Я экспериментировал с вариативными шаблонами 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, не распаковывая его. Является ли это возможным? Если нет, то по какой-то причине это запрещено?


person Luc Touraille    schedule 14.01.2011    source источник


Ответы (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()
{}
person GManNickG    schedule 14.01.2011
comment
Очень хороший обходной путь, я не думал об использовании частичной специализации шаблонов! - person Luc Touraille; 15.01.2011
comment
@GMan: быстрый вопрос ... это было полезно, но должна ли частично специализированная версия быть typedef typename convert_in_tuple<Args...>::type type;, или это не имеет значения? - person Jason; 20.08.2011
comment
@ Джейсон: Верно. Я удивлен, что мой ответ так долго оставался незамеченным зорким глазом. :) - person GManNickG; 20.08.2011
comment
Баллерное решение! Я люблю безумие C ++. - person Casey Rodarmor; 26.02.2012
comment
Я был бы немного обеспокоен: хотя очень заманчиво сказать, что относиться к списку типов или экземпляру определенного типа, содержащему список, как к одному и тому же, по моему опыту, вещи имеют тенденцию беспорядочно взорваться, когда вы это делаете . В качестве примера представьте себе список длиной 1, содержащий variadic_typedef, и то, как он взаимодействует с приведенным выше кодом. Теперь представьте список типов, каждый из которых передается в convert_in_tuple, и то, как он взаимодействует с приведенным выше кодом. Если вы начнете настраивать несколько уровней косвенного обращения, рассмотрение контейнера и его содержимого как взаимозаменяемых вызывает проблемы. - person Yakk - Adam Nevraumont; 12.08.2013
comment
Я не понимаю, как это решает проблему OP. Структура convert_in_tuple содержит псевдоним псевдонима кортежа. Тип, который он представляет, является кортежем с пакетом параметров Args ..., а не самим пакетом параметров Args ... - person user2813810; 28.01.2015
comment
@ user2813810: Сам пакет параметров представляет собой variadic_typedef, convert_in_tuple - это лишь один из примеров того, как вернуть пакет параметров в удобном для использования виде. - person Ben Voigt; 27.04.2020

Я думаю, что это запрещено, потому что это будет беспорядочно, и вы можете обойти это. Вам нужно использовать инверсию зависимостей и сделать структуру, хранящую пакет параметров в шаблоне фабрики, способной применять этот пакет параметров к другому шаблону.

Что-то вроде:

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;
person Ben Voigt    schedule 14.01.2011
comment
Я пробовал ваш код на GCC 4.5, вам просто нужно изменить typename T в class T и изменить параметр convert_in_tuple на параметр шаблона шаблона: template < template< template < typename ... > class > class T > struct convert_in_tuple {...} (!). - person Luc Touraille; 14.01.2011
comment
@Luc: Отредактировано как параметр шаблона шаблона шаблона. Замена typename на class выглядит немного сомнительной, поскольку в черновике говорится, что нет семантической разницы между class и template в параметре-шаблоне., не могли бы вы попробовать этот новый код? - person Ben Voigt; 14.01.2011
comment
Я не могу найти его в стандарте, но я думаю, что помню, что для параметров шаблона шаблона вам нужно использовать class, а не typename (потому что тип шаблона неизбежно является классом, а не каким-либо типом). - person Luc Touraille; 14.01.2011
comment
@Luc: Получил для компиляции в gcc 4.5.2 на виртуальной машине, спасибо за указатели. Теперь изо всех сил пытаюсь заставить работать копирование + вставку из виртуальной машины ... - person Ben Voigt; 14.01.2011
comment
Действительно, стандарт говорит в §14.1.2, что нет разницы между class и typename, но чуть выше (в §14.1.1) синтаксис разрешает только ключевое слово class в объявлении параметра шаблона шаблона. Несмотря на то, что это может показаться непоследовательным, я думаю, что объяснение, как я уже сказал ранее, заключается в том, что параметр шаблона шаблона не может быть любого типа (например, он не может быть int или bool), поэтому, возможно, комитет решил, что использование typename будет вводить в заблуждение. В любом случае, вернемся к теме :)! - person Luc Touraille; 14.01.2011
comment
Ваше решение хорошее, но грустно, что нам нужно использовать такие обходные пути ... Однако это правда, что поддерживать это чистым способом могло быть немного сложно, это означало бы, что typedef может быть либо простым типом или пакет параметров, что странно ... Возможно, можно было использовать новый синтаксис, например "упакованный typedef" (typedef ...Args args или что-то в этом роде). - person Luc Touraille; 14.01.2011

Я нашел идею Бена Фойгта очень полезной в моих собственных усилиях. Я немного изменил его, чтобы сделать его общим не только для кортежей. Для читателей это может быть очевидная модификация, но, возможно, стоит показать:

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 связано с тем, что тип теперь создается с помощью предыдущего списка.

person Werner Erasmus    schedule 04.09.2013

Это вариант изящного трюка с частичной специализацией GManNickG. Никакого делегирования, и вы получаете большую безопасность типов, требуя использования вашей структуры varadic_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() {}
person jack    schedule 18.02.2013
comment
В ответе @ GManNickG не было рекурсии, и я думаю, что возможность использовать необработанный пакет параметров вместо variadic_typedef должна была быть функцией. Следовательно, я бы сказал, что этот ответ - скорее деградация, чем уточнение ... - person Luc Touraille; 18.02.2013
comment
Я понимаю, что вариант отказа от использованияariadic_typedef был задуман как функция, но функция одного человека - ошибка другого человека. Причина, по которой я был в этой теме, в первую очередь, заключалась в том, чтобы найти способ сделать именно то, что здесь делает мой ответ. Кроме того, в решении @ GManNickG есть единственный рекурсивный вызов - когда частичная специализация varadic_typdef convert_in_tuple делегирует неспециализированную версию. Без него все немного проще. И, наконец, я выбрал слово «уточнение» не для того, чтобы представить свое решение как лучшее, а как более конкретное. Я изменил свою формулировку, чтобы отразить это. - person jack; 05.03.2013
comment
Вы можете удалить зависимость от variadic_typedef для convert_in_tuple - пусть это займет template<typename Pack> struct convert_in_tuple {};, затем специализируется template<template<typename...>class Pack, typename...Args> struct convert_in_tuple<Pack<Args...>> { typedef std::tuple<Args> type; } - теперь любой вариантный пакет может быть сопоставлен с tuple. - person Yakk - Adam Nevraumont; 12.08.2013