我对以下可变参数代码有两个问题:
#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;
}
- 为什么对参数使用常量引用?
- 当我使用浮点数和整数组合时,例如
add(2.3, 43, 32.2)
它不会向我显示正确的值。 我该如何解决?
问题 1 的答案
由于某些类型的复制成本很高,因此如果您只是执行求和,这不会修改基础对象,则通过 const 引用传递可以消除复制对象的开销。
例如,如果按值传递一个大vector
,则整个vector
将被复制以调用该函数,而传递 const 引用要快得多(可能实现为指针复制(。
对问题2的回答
你的问题是当你计算43 + 32.2
返回值是int
,丢弃小数部分。这是因为在这种情况下T
被推导出为文字43
的类型,即int
。针对您的问题的两种解决方案:
- 使用
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 中编写自己的 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...>
。
例如,这适用于带有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
。
-
你为什么不通过引用传递参数?对于整数来说,这并不重要,但我也可以
Add(std::string{"QWER"}, std::string{"ASDF"}, std::string{"ZXCV"})
,复制它们可能会变得非常昂贵。 -
无论如何,您始终返回左侧类型。这意味着如果你有
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...);
}
对于简单类型,两者都可以。 如果复制成本很高T
const&
效率会更高。
当我使用带有 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
}