Я пытаюсь освоить вариативные параметры функции / шаблона. Однако в двух приведенных ниже функциях я очень смущен тем, почему SumIndices не компилируется (я получаю сообщение об ошибке компилятора "шаблон расширения 'std :: size_t' {aka 'long unsigned int'} не содержит пакетов параметров ") в то время как SumValues делает.

template <typename ...t_data_type>
constexpr auto SumValues(t_data_type ..._values) { 
  return (_values + ...); 
}

constexpr auto SumIndices(std::size_t ..._indices) { 
  return (_indices + ...); 
}

Я был бы очень признателен, если бы кто-нибудь мог прояснить мне эту путаницу!

1
niran90 25 Окт 2021 в 00:12
2
Вы не можете использовать пакеты параметров без шаблонов. Ваша попытка перегрузки для size_t имеет недопустимый синтаксис. См. эту публикацию SO.
 – 
Mansoor
25 Окт 2021 в 00:24
Хорошо, тогда как лучше всего создать шаблон SumIndices, если он должен принимать только аргументы типа size_t?
 – 
niran90
25 Окт 2021 в 00:27
2
Я не знаю, что вы имеете в виду под шаблонами, ваша перегрузка не нужна, вы можете удалить ее и не потерять никакой функциональности. Если вы вызовете первую функцию с переменным числом аргументов с аргументами size_t, она будет работать.
 – 
Mansoor
25 Окт 2021 в 00:29
Ну ладно, понятно. Спасибо что подметил это!
 – 
niran90
25 Окт 2021 в 00:31
2
Да, если я правильно понял вашу проблему. Если вы хотите выполнить проверку типов, вы можете сделать это с помощью SINFAE, Concepts (C ++ 20) или static_assert внутри функции.
 – 
Mansoor
25 Окт 2021 в 00:39

2 ответа

Лучший ответ

В первом случае у вас есть пакет параметров. Во втором случае у вас есть вариативная функция из C.

Шаблоны с переменным числом аргументов позволяют безопасно вводить различные типы в вашу функцию. Пример печати с этим:

// Create this function to terminate argument depended lookup
void PrintValues(std::ostream&){}
template<typename TFirstArg, typename ...TArgs>
void PrintValues(std::ostream& output, const TFirstArg& arg, const TArgs... other_args){
   // Recursive call to another function which has N-1 args
   // If other_args are empty, it would call `void PrintValues(std::ostream&)`
   // If they are non empty, it would call another instantiation of this template
   PrintValues(output << arg, other_args...);
}

А это можно назвать так:

PrintValues(std::cout, 5LL, 7.59, "Hello world", std::string{"bingo"});

С помощью varargs вы можете сделать это:

void PrintFewNumbers(size_t number_of_args, ...)
{
    va_list args;
    va_start(args, number_of_args);
    for (size_t idx_of_arg, idx_of_arg < number_of_args; ++idx_of_arg){
        size_t current_arg = va_arg(args, size_t);
        std::cout << current_arg;
    }
    va_end(args);
}

И вы можете вызвать это, используя это:

PrintFewNumbers(0);
PrintFewNumbers(5, 1,2,3,4,5);

Вам следует предпочесть вариативные шаблоны вариативным аргументам, потому что они безопасны по типу. Однако их не всегда можно использовать.

4
Angelicos Phosphoros 25 Окт 2021 в 00:37

С C ++ 20 вы можете легко создать свой SumIndices с помощью библиотеки концепций :

#include <concepts>
auto SumIndices(std::same_as<std::size_t> auto ... indices);

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

Вместо этого вы можете подумать об использовании std::integral или std::unsigned_integral, что позволит использовать другие интегральные типы:

auto SumIndices(std::integral auto ... indices);
4
Ranoiaetep 25 Окт 2021 в 04:09
Очень здорово! Спасибо за этот совет :)
 – 
niran90
25 Окт 2021 в 09:20
1
Я предпочитаю использовать auto SumIndices(std::convertible_to<std::size_t> auto... indices);
 – 
康桓瑋
25 Окт 2021 в 13:36
Не могли бы вы уточнить, почему std::convertible_to<std::size_t> предпочтительнее альтернатив в приведенном выше ответе?
 – 
niran90
25 Окт 2021 в 13:53
1
@ niran90. Потому что он может принимать std::integral_constant<int, 0>{} и интуитивно выражать ожидаемый тип (std::size_t), который будет принят функцией.
 – 
康桓瑋
25 Окт 2021 в 14:23