对可变参数模板使用 const 参数



我对以下可变参数代码有两个问题:

#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)它不会向我显示正确的值。 我该如何解决?

问题 1 的答案

由于某些类型的复制成本很高,因此如果您只是执行求和,这不会修改基础对象,则通过 const 引用传递可以消除复制对象的开销。

例如,如果按值传递一个大vector,则整个vector将被复制以调用该函数,而传递 const 引用要快得多(可能实现为指针复制(。


对问题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...);
}
  1. 或者如果你有 c++ 17,只需使用折叠表达式
template <typename... Pack>
auto Add(const Pack&... arg_list)
{
return (... + arg_list);
}
  1. 如果您使用的是 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 中编写自己的 sum 类型推断器:

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...>

例如,这适用于带有chars 的包,其中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;
}

将输出:int394

  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...);
}

1(它只是在不复制的情况下接收参数。const只是为了让它们"只读",它们不能被更改。此外,当您使用 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 + ...);
}

为什么对参数使用常量引用?

当您打算将其调用为:

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...);
}

对于简单类型,两者都可以。 如果复制成本很高Tconst&效率会更高。

当我使用带有 add(2.3, 43, 32.2( 等整数组合的浮点数时,它不会向我显示正确的值。 我该如何解决?

您可以使用43.0而不是43

使用Add(2.3, 43, 32.2)时,递归调用Add(43, 32.2)返回一个int并将返回值截断为75

关于错误的和...问题是

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
}

相关内容

最新更新