Каким-то образом «используя» список типов (в C 17 и выше)

#c #templates #types #variadic-templates

#c #шаблоны #типы #переменные-шаблоны

Вопрос:

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

 template <typename ...Types>
void Function()
{ /* do something that depends on the Types */ }

void Test()
{

    using my_types = { int, float };    // not possible
    using my_types = int, float;        // alternative disallowed syntax
    Function<my_types>();
    
    Function<int,float>();              // OK, but I can't specify manually

}
  

Почему нет прямой поддержки для списков типов такого типа? Каков простой обходной путь?

Примечания

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

Для пояснения варианта использования: пользователь определяет класс, подобный признаку, в котором он каким-то образом определяет список типов. Позже мне нужно обработать этот список. То, как он это определяет, все еще открыто. Итак, ищу простой способ сделать это. Нет необходимости в чрезмерно сложном шаблоне «объединить список типов во время компиляции», который можно найти где-то здесь.

Комментарии:

1. Есть списки типов, смотрите любую книгу по метапрограммированию шаблонов в C , template <class… Ts> struct list { используя type = list <Ts…>; };

Ответ №1:

Возможной альтернативой является определение своего рода оболочки типа (как std::tuple но которая абсолютно ничего не делает с аргументами шаблона)

 template <typename...>
struct type_wrapper
 { };
  

и объявляем Function() получение объекта этого типа

 template <typename ...Types>
void Function (type_wrapper<Types...> const amp;)
{ /* do something that depends on the Types */ }
  

таким образом, вы можете передать объект желаемой оболочки в Function() и позволить шаблонному вычету работать

 using my_wrapped_types = type_wrapper<int, float>;

Function(my_wrapped_types{}); 
  

Почему нет прямой поддержки для списков типов такого типа? Каков простой обходной путь?

Потому что есть std::tuple которые охватывают большинство вариантов использования и, как вы можете видеть, тривиально написать оболочку, когда вы хотите что-то более легкое.

Я не хочу передавать объекты этих типов в функцию.

Таким образом, вы передаете объект типа type_wrapper , но экземпляр ссылочного типа не создается.

Комментарии:

1. Ах, facepalm я уже использую это для другой цели в том же коде. На этот раз мне не удалось перевести свой мозг в режим обратной логики. Итак, вы говорите, что поскольку обходной путь / оболочка настолько проста (т. Е. «прямолинейна»), никто никогда не заботился о том, чтобы включить что-то подобное using types = A,B,... в стандарт!?

2. @не-пользователь38741 — Да, я полагаю, это причина. Добавление этого std::tuple охватывает множество вариантов использования.

3. @не-пользователь38741 — Что касается возможного using types = A,B,... синтаксиса … нет, язык не поддерживает его так просто. Я не языковой уровень, поэтому я точно не знаю, почему.

4. Необходимость создания и передачи объекта, который на самом деле не нужен (кроме вывода типов), настолько нелогична. (Очевидно, это не ваша вина. Ответ в порядке) Если бы можно было вызвать Function<type_wrapper<A,B>>() , а затем каким-то образом получить доступ к A и B, это было бы скорее прямой логикой. Возможно, это возможно с tuple в качестве оболочки.

5. @не-пользователь38741 — О, да: это возможный вызов Function<type_wrapper<A, B>>() . Проблема в том, что вы не можете перехватить (в качестве параметров шаблона функции) A и B . Вы перехватываете T = type_wrapper<A, B> . Ничто не запрещает вам писать некоторые пользовательские свойства типа для извлечения A и B из T , но это громоздко. Лучше пройти через class / struct , где, используя частичную специализацию, вы можете перехватить A и B более простым способом (см. Ответ Jarod42).

Ответ №2:

Возможны обходные пути, в основном использующие std::tuple :

 template <typename... Types>
void Function()
{ /* do something that depends on the Types */ }

template <typename... Types>
struct FunctionHelper<std::tuple<Types...>>
{ void operator ()() const { Function<Types...>(); } };

void Test()
{
    using my_types = std::tuple<int, float>;
    FunctionHelper<my_types>{}();
}
  

Я не хочу передавать объекты этих типов в функцию

Возможный обходной путь — использовать struct для этого, как std::type_identity (C 20, но его легко переписать)

и затем

 void Test()
{
    constexpr auto types = std::tuple{std::type_identity<int>{}, std::type_identity<float>{}};
    std::apply([](auto... args){ Function<typename decltype(args)::type...>(); }, types);
}

  

Комментарии:

1. Иисус Христос, спасибо. Как я мог не видеть, что это снова tuple на помощь!? (((у вас там опечатка с using type vs <my_types> )))

2. Как насчет того, чтобы Function<tuple<A,B>>() а затем иметь std::apply внутри функции? Не моя исходная спецификация, но от этого можно избавиться FunctionHelper , или нет? A,B Тогда они недоступны?

3. std::apply ожидает std::tuple экземпляр. Это не может быть применено напрямую (как вы сказали, типы могут не создаваться по умолчанию).