Передать аргумент шаблона шаблона без указания конкретного типа - PullRequest
0 голосов
/ 18 октября 2018

Я хотел бы реализовать обертку вокруг вызова функции, которая будет делать что-то вроде этого:

template <template<class> class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f<std::int8_t>();
            break;
        case 2:
            f<std::int16_t>();
            break;
        default:
            break;
    }
}

template <class T>
void func()
{
    // ... body of func()
}

, чтобы я мог сделать следующий вызов в коде:

Wrapper(1, func);

Но вышеупомянутый код не компилируется, потому что конструкция F&& f недопустима - мне нужно указать конкретный тип аргумента.Но если я сделаю подпись функции следующим образом:

template <template<class> class F, class T>
void Wrapper(int n, F<T>&& f)

, тогда я должен сделать вызов с конкретным типом f:

Wrapper(1, func<std::int8_t>);

, и я не смогусделать переключение в Wrapper.

Как мне реализовать нужное мне поведение?

Ответы [ 5 ]

0 голосов
/ 18 октября 2018

и я не смогу переключиться в Wrapper.

Как мне реализовать нужное мне поведение?

К сожалению, вы не можете пройтифункция шаблона, другая функция, без исправления типов шаблонов.

Но есть много способов сделать то, что вы хотите, используя шаблоны классов (или структуры).

Например, следуя вашему запросуосновываясь на аргументе template-template, вы можете поместить статическую функцию в структуру шаблона (я предлагаю static, так что вам не нужен объект этого типа)

template <typename>
struct foo
 {
   static void func ()
    {
      // ... body of func()
    }
 };

и вам нужно передать foo объясняя это как параметр шаблона

Wrapper<foo>(1);
Wrapper<foo>(2);

Wrapper() становится

template <template <typename> class F>
void Wrapper (int n)
{
    switch (n)
    {
        case 1:
            F<std::int8_t>::func();
            break;
        case 2:
            F<std::int16_t>::func();
            break;
        default:
            break;
    }
}

Другой способ - создать не шаблонный класс / структуру с шаблоном

struct bar
 {
   template <typename>
   static void func ()
    {
      // ... body of func()
    }
 };

, который вы можете использовать таким же образом

Wrapper<bar>(1);
Wrapper<bar>(2);

или, если хотите, при выводе типа с использованием bar объекта

bar  b0;

Wrapper(bar{}, 1);
Wrapper(b0,    2);

В первом случае подписьдля Wrapper это просто

template <typename F>
void Wrapper (int n)

, где во втором случае становится

template <typename F>
void Wrapper (F const &, int n)

В обоих случаях вам нужно объяснить template, вызывающий func()

        F::template func<std::int8_t>();

        // ...

        F::template func<std::int16_t>();
0 голосов
/ 18 октября 2018

В C ++ существует несколько видов имен.

Есть значения.Есть типы.Есть функции (и методы).Есть участники.Есть шаблоны типов.Есть шаблоны функций.Существуют переменные шаблоны.

Это не официальные имена в стандарте.

В случаях, когда шаблон создается, вы получаете то, что создает шаблон.Таким образом, шаблон типа при создании экземпляра дает тип и т. Д.

Члены (функции и переменные) могут стать значениями, став указателем на функцию / значение члена.Имена переменных-членов могут стать значениями при сопряжении с объектом, например foo.x.

Имя функции может стать значением путем разрешения перегрузки.В некоторых случаях имя функции не перегружено, поэтому разрешение перегрузки является тривиальным.

И т. Д.

Аргументы функции всегда являются значениями.Они никогда не являются шаблонами.

Аргументы шаблона могут быть типами, шаблонами типов или значениями.

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

Шаблоны функций - очень граждане второго сорта в C ++.Вы не можете передать шаблон функции в любой другой шаблон, а также не можете передать их в качестве значения.То, что вы можете сделать с ними, сильно ограничено.

Поэтому нам нужно обернуть шаблон во что-то, что можно передать.

Вот один из подходов:

template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t=typename Tag::type;

auto func_template = [](auto...tags) {
  return [](auto&&...args)->decltype(auto) {
    return func< type_t<decltype(tags)>... >( decltype(args)(args)... );
  };
};

теперь func_template оборачивает шаблонную функцию вида template<class...Ts> R f(Args...) для почти произвольных типов R, Args... и Ts...

Если мы предположим, что наш аргумент обернут этимКстати, мы делаем это:

template <class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f(tag<std::int8_t>)();
            break;
        case 2:
            f(tag<std::int16_t>)();
            break;
        default:
            break;
    }
}

, затем в точке вызова мы делаем:

Wrapper( 2, func_template );

и это работает.

Мы можем сгенерировать func_template, используя макросы:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  ->decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }

#define FUNC_TEMPLATE(...) \
[](auto...tags) { \
  return [](auto&&...args) \
    RETURNS( __VA_ARGS__< type_t<decltype(tags)>... >( decltype(args)(args)... ) ); \
}

и теперь мы можем изменить сайт вызова на:

Wrapper( 2, FUNC_TEMPLATE(func) );
0 голосов
/ 18 октября 2018

Вы можете переписать свой код в что-то вроде

template <typename T> struct tag { using type = T;};

template <class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f(tag<std::int8_t>());
            break;
        case 2:
            f(tag<std::int16_t>());
            break;
        default:
            break;
    }
}

template <class T>
void func()
{
    // ... body of func()
}

С использованием:

Wrapper(1, [](auto t){ return func<typename decltype(t)::type>()});
0 голосов
/ 18 октября 2018

Если вы знаете func во время компиляции (то есть, если это не какой-то указатель на функцию), вы можете использовать следующее решение:

template <template <class> typename F>
void Wrapper(int n) {
  switch (n) {
    case 1: F<std::int8_t>{}(); break;
    case 2: F<std::int16_t>{}(); break;
    default: break;
  }
}

template <typename T>
void func() { std::cout << sizeof(T) << std::endl; }

template <typename T>
struct Func { void operator()() { func<T>(); } };

int main() {
  Wrapper<Func>(1);
  Wrapper<Func>(2);
}
0 голосов
/ 18 октября 2018

Можете ли вы перейти int n к параметру шаблона?Тогда вы можете использовать Int2Type идиома:

template<int n> struct ParamType {
    using value = ... // here will be std::int8_t or std::int16_t depending on n
}

template <template<class> class F, int n>
void Wrapper(F f) {
    f<ParamType<n>::value>();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...