Как да напиша функция с променлив шаблон в C++, където пакетът с параметри не е последният параметър?

Опитвам се да използвам функция с променлив шаблон, където пакетът с параметри не е последният параметър в списъка. Имайте предвид, че има две рекурсивни извиквания – едното изпуска параметър пред пакета, другото извикване изпуска параметър след пакета.

  • Моят компилатор изглежда е: Apple LLVM версия 8.1.0 (clang-802.0.42)
  • Всички int по-долу ще бъдат нов T шаблонен параметър, ако мога да накарам това да работи.

Няма смисъл да използвате ... ако сайтът за повикване за Blender не може да бъде чист. В този случай мога просто да разширя няколко претоварвания на Blender. Наистина предпочитам да не прибягвам до това. Надявам се, че пропускам нещо.

int Blender( double t, int i)
{
    return i;
}

template <typename ...Args>
  int Blender( double t, int first, Args... more, int last)
{
    return (1-t)*Blender(t, first, more...) + t*Blender(t, more..., last);
}

static void tryit()
{
    Blender(.5, 23, 42, 89); //doesn't compile
}

person Mustang    schedule 17.12.2017    source източник
comment
Накратко, не е възможно. Пакетите са алчни по дизайн.   -  person StoryTeller - Unslander Monica    schedule 17.12.2017
comment
Може да е в състояние да го направи подобно на това, как може да се направи къри в C++. Обратът е да извадите този последен параметър и да направите каквото и да е специално нещо, което искате да направите с него. stackoverflow.com/questions/152005 /   -  person Eljay    schedule 17.12.2017
comment
Откъде идват тези числа? Това е възможно само когато работите с константи по време на компилиране.   -  person Jodocus    schedule 17.12.2017
comment
@Jodocus, в действителност те изобщо не са числа. Те са обекти, които могат да се добавят и умножават по двойно (линейно комбинирани). И стойностите не са известни по време на компилиране.   -  person Mustang    schedule 17.12.2017
comment
Вашата версия на xcode поддържа някои функции на c++14, ако я включите. Това ще помогне, ако можете да го направите; можеш ли да го направиш?   -  person Yakk - Adam Nevraumont    schedule 18.12.2017
comment
Възможен дубликат на Variadic шаблон на функция с разширение на пакета, което не е последно параметър   -  person underscore_d    schedule 18.12.2017


Отговори (3)


Предлагам друго решение, наистина различно от кода във въпроса, което според мен е много по-ефективно (създаване само на един std::array; блендер, получен чрез последователност от индекси на шаблони)

#include <array>
#include <utility>
#include <iostream>

template <typename T, std::size_t I0>
int blenderH (double t, T const & arr, std::index_sequence<I0> const &)
 { return arr[I0]; }

template <typename T, std::size_t I0, std::size_t ... Is>
auto blenderH (double t, T const & arr, 
               std::index_sequence<I0, Is...> const &)
   -> std::enable_if_t<0U != sizeof...(Is), int>
 { return   (1-t) * blenderH(t, arr, std::index_sequence<(Is-1U)...>{})
          +    t  * blenderH(t, arr, std::index_sequence<Is...>{}); }

template <typename ... Args>
int blender (double t, Args ... as)
 {
   static constexpr auto size = sizeof...(Args);

   return blenderH(t, std::array<int, size>{ { as... } },
                   std::make_index_sequence<size>{});
 }

int main()
 { std::cout << blender(.3, 23, 42, 89) << std::endl; }

За съжаление и това решение работи (std::index_sequence и std::make_index_sequence), като се започне от C++14.

-- РЕДАКТИРАНЕ --

Калет казва.

Някакво обяснение на случващото се тук би помогнало. Объркан съм от липсата на I0 в тялото на второто претоварване.

Опитвам се с обяснение.

Да предположим, че се нарича рекурсивна версия на BlenderH() (второто претоварване) със списък от индекси в стойността std::index_sequence. Кажете 5, 6, 7 и 8; така че I0 е 5 и Is... е 6, 7, 8.

Трябва да извикаме рекурсивно blenderH() с индекси 5, 6, 7 първо и с 6, 7, 8 следващо.

Можем да избегнем използването на I0 (5), защото

  • 5, 6, 7 се получава от 6, 7, 8 намаляване с 1 на всяка стойност (така че std::index_sequence<(Is-1U)...> за първото рекурсивно повикване)

  • и 6, 7, 8 е Is... без модификации (така че std::index_sequence<Is...> във втория.

От практическа гледна точка I0 се обявява само за изхвърляне; няма нужда да го използвате.

person max66    schedule 17.12.2017
comment
Някакво обяснение на случващото се тук би помогнало. Объркан съм от липсата на I0 в тялото на второто претоварване. - person Caleth; 18.12.2017
comment
@Caleth - Не съм добър в обясненията, но... отговорът е променен. - person max66; 19.12.2017

template<std::size_t I>
using count = std::integral_constant<std::size_t, I>;

namespace details {

  template<class...Args, std::size_t N,
    typename std::enable_if<sizeof...(Args)==N, bool>::type = true
  >
  int Blender( count<N> drop, double t, int i, Args...args ) {
    return i;
  }
  template<class...Args, std::size_t N,
    typename std::enable_if<sizeof...(Args)!=N, bool>::type = true
  >
  int Blender( count<N> drop, double t, int i, Args...args ) {
    return (1-t)*Blender( count<N+1>{}, t, i, args... ) + t*Blender( count<N>{}, t, args... );
  }
}

template <typename ...Args>
int Blender( double t, int first, Args... more)
{
  return details::Blender( count<0>{}, first, more... );
}


static void tryit()
{
  Blender(.5, 23, 42, 89); //doesn't compile
}

тук count<N> брои броя на аргументите в края, които да се игнорират.

Двете details претоварвания покриват случая, когато N е равно на броя на аргументите в пакета (и следователно ни остава 1 аргумент), и когато не е. Те се изпращат с помощта на SFINAE.

person Yakk - Adam Nevraumont    schedule 18.12.2017

Това всъщност е много лесно, след като осъзнаете, че ограничението е в извеждането на пакета тип, а не в извикването на функция. Просто се нуждаем от начин да използваме пакета типове, който можем да изведем и да го предадем изрично, избягвайки дедукцията във функцията, която трябва да извлече последния параметър:

int Blender( double t, int i)
{
    return i;
}

template <typename ...Args>
  int Blender( double t, int first, Args... more);

template <typename ...Args>
  int BlenderWithoutLast( double t, Args... more, int last)
{
    return Blender(t, more...);
}

template <typename ...Args>
  int Blender( double t, int first, Args... more)
{
    return (1-t)*BlenderWithoutLast<Args...>(t, first, more...) + t*Blender(t, more...);
    //    all the magic happens here ^^^^^
}

и сега вашият тестов случай се компилира и изпълнява

#include <iostream>
int main()
{
    std::cout << Blender(.5, 23, 42, 89);
}

За мен това работи с clang и --std=c++11

person Ben Voigt    schedule 18.12.2017
comment
Чудесен! Ubi maior minor cessat. - person max66; 19.12.2017
comment
@max66: Опитвам се да разбера как да ви призная, че първо сте помислили за помощната функция, защото нямаше да намеря действителния параметър за преминаващ шаблонен тип, който го прави толкова по-прост без вашия намек в посока на помощна функция, структурирана да прави това. - person Ben Voigt; 19.12.2017