Использование параметра const для шаблона переменной - PullRequest
0 голосов
/ 01 октября 2019

У меня есть два вопроса о следующем коде переменной:

#include <iostream>

template <typename T>
T Add(const T& arg_a)
{
    return arg_a;
}

template <typename T, typename... Pack>
T Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}

int main(int argc, const char* argv[])
{

    auto itLocalSum = Add(1, 2, 3, 4, 5);
    std::cout << "Sum of Add: " << itLocalSum << "\n";

    return 0;
}
  1. Почему используется константная ссылка для параметров?
  2. Когда я использую плавающие числа с комбинацией целых чиселкак add(2.3, 43, 32.2) это не показывает правильное значение для меня. как я могу это исправить?

Ответы [ 5 ]

10 голосов
/ 01 октября 2019

Ответ на вопрос 1

Поскольку некоторые типы копируются дорого, если вы просто делаете суммирование, которое не изменяет базовые объекты, передача по константной ссылке исключаетстоимость копирования объектов.

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


Ответ на вопрос 2

Ваша проблема в том, что при вычислении 43 + 32.2возвращаемое значение int, отбрасывая десятичные части. Это связано с тем, что T в этом контексте выводится как тип литерала 43, то есть int. Два решения для вашей проблемы:

  1. Используйте auto в качестве возвращаемого значения. (Если вы на C ++ 14)
template <typename T>
T Add(const T& arg_a)
{
    return arg_a;
}

template <typename T, typename... Pack>
auto Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}
Или, если у вас есть c ++ 17, просто используйте сложенные выражения
template <typename... Pack>
auto Add(const Pack&... arg_list)
{
    return (... + arg_list);
}
Если вы используете c ++ 11, используйте std :: common_type
template <typename T>
T Add(const T& arg_a)
{
    return arg_a;
}

template <typename T, typename... Pack>
typename std::common_type<T, Pack...>::type Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}

Когда std::common_type не работает (например, Add('a', 'b', 'c', 'd')),вы все еще можете написать свой собственный дедуцирующий тип суммы в c ++ 11:

template <typename SumLeftT, typename ... Args>
struct sum_t_impl;

template <typename SumLeftT>
struct sum_t_impl<SumLeftT> {
    using type = SumLeftT;
};

template <typename SumLeftT, typename FirstT, typename ... Rest>
struct sum_t_impl<SumLeftT, FirstT, Rest...> {
    using type = typename sum_t_impl<decltype(std::declval<SumLeftT>() + std::declval<FirstT>()), Rest...>::type;
};

template <typename T, typename ... TArgs>
using sum_t = typename sum_t_impl<T, TArgs...>::type;

и заменить тип возвращаемого значения typename std::common_type<T, Pack...> на sum_t<T, Pack...>.

Это, например, работает дляПакет с char s, где char + char -> int.

int main()
{
    auto itLocalSum = Add('a', 'b', 'c', 'd');
    std::cout << typeid(itLocalSum).name() << std::endl;
    std::cout << "Sum of Add: " << itLocalSum << "\n";
    return 0;
}

Будет выводить: int и 394.

2 голосов
/ 01 октября 2019
  1. Почему не вы передаете аргументы по ссылке? Для целых чисел это не имеет значения, но я также могу Add(std::string{"QWER"}, std::string{"ASDF"}, std::string{"ZXCV"}), а копирование их может обойтись довольно дорого.

  2. Вы всегда возвращаете тип левой стороны, независимо от того, что,Это означает, что если у вас есть Add(2, 3.5), результат должен быть int, и он будет приведен к такому.

Чтобы избежать этого, вы можете вывести возвращаемый тип:

template <typename T, typename... Pack>
auto Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}
2 голосов
/ 01 октября 2019

1) Это просто для получения параметров без копирования. const только для того, чтобы сделать их «только для чтения», их нельзя изменить. Кроме того, когда вы используете константную ссылку, вы можете связать аргументы rvalue с параметрами lvalue, затем вы также можете передать литералы в вашу функцию, например.

2) Существует неявное преобразование в int всякий раз, когда первый параметрцелое число для каждой пары распаковки. Если вы измените значение 43 на 43.0, оно будет работать. Кроме того, в C ++ 17 вы можете отказаться от функции Add и использовать выражение сгиба: return (arg_a + ... + arg_list);.

Возможное лучшее решение:

template <typename... Pack>
auto Add(const Pack&... arg_list) {
    return (arg_list + ...);
}
1 голос
/ 01 октября 2019

Почему используется константная ссылка для параметров?

Когда вы намереваетесь вызвать его как:

auto itLocalSum = Add(1, 2, 3, 4, 5);

аргументы должны быть const& илипросто ценности. то есть

template <typename T>
T Add(const T& arg_a)
{
    return arg_a;
}

template <typename T, typename... Pack>
T Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}

или

template <typename T>
T Add(T arg_a)
{
    return arg_a;
}

template <typename T, typename... Pack>
T Add(T arg_a, Pack... arg_list)
{
    return arg_a + Add(arg_list...);
}

Для простых типов подойдет любой. const& будет более эффективным, если копировать T дороже.

Когда я использую плавающие числа с комбинацией целых чисел, таких как add (2.3, 43, 32.2), она не будет отображаться правильноценность для меня. как я могу это исправить?

Вы можете использовать 43.0 вместо 43.

Когда вы используете Add(2.3, 43, 32.2), рекурсивный вызов Add(43, 32.2) возвращает int и усекает возвращаемое значение до 75.

1 голос
/ 01 октября 2019

Относительно неправильной суммы ... проблема в том, что

template <typename T, typename... Pack>
T Add(const T& arg_a, const Pack&... arg_list)
{
    return arg_a + Add(arg_list...);
}

возвращает значение того же типа, что и первый аргумент (T).

Итак, если T - это целое число, вы теряете части с плавающей запятой в следующих аргументах.

В случае, если

Add(2.3, 43, 32.2)

, вы получите, что после 43, 32.2 станет 32.

Чтобы решить эту проблему ... если вы можете использовать C ++ 17, вы можете использовать свертывание шаблонов и просто написать

template <typename ... Ts>
auto Add (Ts const & ... as)
 { return (as + ...); }

Если вы не можете использовать C ++17, но вы можете использовать C ++ 14, вы можете смоделировать сворачивание шаблона следующим образом

template <typename ... Ts>
auto Add (Ts const & ... as)
 {
   using unused = int[];

   typename std::common_type<Ts...>::type ret{};

   (void)unused { 0, ((void)(ret += as), 0)... };

   return ret;
 }

Обратите внимание на использование std::common_type<Ts...>::type для получения переменной возвращаемого типа.

ЕслиВы можете использовать только C ++ 11 (поэтому без auto без завершающего возвращаемого типа) вы должны явно указать возвращаемый тип

template <typename ... Ts>
typename std::common_type<Ts...>::type Add (Ts const & ... as)
 { 
   // same body as in C++14
 }
...