Создать пакет параметров складного шаблона

Вопрос

Можно ли создать складной (※ сложить выражение) шаблон пакет параметров?

Пример

Рассмотрим следующий пример (функция, которая принимает два аргумента типа int (разложившийся)).

template<
    typename L,
    typename R,
    typename = std::enable_if_t<
            std::is_same_v<int, std::decay_t<L>>
        &&  std::is_same_v<int, std::decay_t<R>>
    >
>
int F(L Left, R Right){
    return 0x70D0;
}

Можно ли создать пакет параметров шаблона, который можно свернуть, чтобы не писать один и тот же фрагмент кода несколько раз (т.е.std::is_same_v)?

Что-то, что представлено ниже как std::pack, может упростить использование SFINAE?

typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<std::pack<L, R>>>)>

Что я пробовал

Я попытался решить проблему, используя пакет T и псевдонимы одиночных L и R. Но по какой-то причине следующий код компилируется и запускается без ошибок (второй аргумент второго вызова функции F, распался, не равен int) на MSVC 15.9.4 + 28307.222:

template<
    typename... T,
    typename L = std::tuple_element_t<0, std::tuple<T...>>,
    typename R = std::tuple_element_t<1, std::tuple<T...>>,
    typename = std::enable_if_t<(... && std::is_same_v<int, std::decay_t<T>>)>
>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // OK, but should not compile
}

PS Кроме того, я что-то пропустил в приведенном выше коде, чтобы SFINAE работала должным образом (функции фильтрации только с аргументами int, int (распавшимися))?

Я голосую за то, чтобы закрыть этот вопрос как не по теме, потому что, по-видимому, ОП больше не хочет, чтобы это было?

Hans Kesting 21.08.2019 09:13

@ Ханс, ТАК работает не так. Если вопрос по теме, то он принадлежит сообществу SO. Мы не удаляем таким образом хороший контент.

Toby Speight 21.08.2019 10:11

@TobySpeight Я не был уверен, откатывать ли удаление вопроса OP или VTC - я откатываюсь в следующий раз

Hans Kesting 21.08.2019 10:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
161
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

На месте? Не в C++ 17. Вам нужно будет обернуть ваши типы в какой-то template <typename...> struct typelist;, а затем развернуть их где-нибудь еще. Это требует одного уровня косвенного обращения.

Насколько я знаю, нет возможности записать что-нибудь вроде std::pack.


I've tried to solve the problem using T pack and aliasing single L and R. [...]

В вашем коде T... всегда будет пустым, поскольку он ничем не выводится. Значения параметров шаблона по умолчанию для L и R игнорируются, поскольку они выводятся при вызове функции.

Вам понадобится что-то вроде:

template<
    typename... T,
    typename = std::enable_if_t<(... && std::is_same_v<int, T>)>
>
int F(T...){
    return 0x70D0;
}

В C++ 20 вы должны иметь возможность использовать лямбда следующим образом:

template<
    typename L,
    typename R,
    typename = std::enable_if_t<[]<typename... Ts>(){
        return (... && std::is_same_v<int, Ts>)
    }.operator()<L, R>()>
>
int F(L Left, R Right){
    return 0x70D0;
}

Можно уронить decay_t<T>, так как вы принимаете по стоимости (decay_t<T> будет просто T)

Barry 19.12.2018 20:16

@Barry: исправлено, также добавлено возможное решение C++ 20.

Vittorio Romeo 19.12.2018 23:55

lol, у меня есть соблазн удалить свой голос за то, что я серьезно предложил это ;-)

Barry 19.12.2018 23:56
std::tuple можно использовать в воображении очень похоже на этот гипотетический std::pack.
metalfox 21.12.2018 08:53

Слишком поздно играть?

Is it possible to create template parameter pack that can be folded to avoid writing same fragment of code multiple times?

Насколько мне известно, не в самом F().

Но вы можете перепаковать типы, например, в список вызываемой функции.

Я имею в виду ... если вы объявлять определите (только объявить: нет необходимости определять его, потому что он используется только в decltype()) следующую функцию [Обновлено: как было предложено Барри (спасибо), определение функции упрощает использование]

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

где вы можете использовать сворачивание шаблонов, вы можете SFINAE для включения / отключения F() следующим образом

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

Ниже приведен полный пример компиляции.

#include <type_traits>

template <typename T, typename ... Ts>
constexpr auto isSameList ()
   -> std::bool_constant<(... && std::is_same_v<T, std::decay_t<Ts>>)>
 { return {}; } 

template <typename L, typename R,
          std::enable_if_t<isSameList<int, L, R>(), bool> = true>
int F(L Left, R Right)
 { return 0x70D0; }

int main ()
 {
    F(3, 5);   // compile
    //F(3, "5"); // compilation error
 }

Этот шаблон работает намного лучше, если вы определите isSameList для возврата этого объекта, поэтому вы можете заменить decltype(f()){} только на f().

Barry 19.12.2018 21:58

@Barry - Ты прав: так проще и элегантнее. Спасибо!

max66 19.12.2018 22:17

У вас это почти было:

template <typename L,typename R,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,L,R>,std::tuple<L,R,int>>>>
int F(L Left, R Right){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

или вариативная версия:

template <typename... T,
  typename = std::enable_if_t<std::is_same_v<std::tuple<int,T...>,std::tuple<T...,int>>>>
int F(T... args){
    return 0x70D0;
}

int main(){
    F(3, 5);   // OK
    F(3, "5"); // Does not compile
    F("5", 3); // Does not compile
}

Другие вопросы по теме