Как написать функцию шаблона с переменным числом аргументов на С++, где пакет параметров не является последним параметром?

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

  • Мой компилятор выглядит следующим образом: 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
Замечательно! Уби майор минор прекращается. - person max66; 19.12.2017
comment
@max66: Пытаюсь понять, как отблагодарить вас за то, что вы сначала подумали о вспомогательной функции, потому что я бы не нашел фактического-параметра-шаблона-типа, который делает его намного проще, без вашего намека в направлении вспомогательная функция, структурированная для этого. - person Ben Voigt; 19.12.2017