可变参数模板分隔符
Variadic template divider
我有以下小的可变模板,它应该划分一系列数字。据我了解,这是我调用 divide(20, 2, 2)
-> 20/ 2 / 2
时会发生什么的粗略描述。显然它不会发生那么好,因为我得到的答案是 20 ...当只有两个参数时它工作正常。
#include <iostream>
template<class first_t>
auto divide(const first_t &first)
{
return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
return first / divide(rest...);
}
int main()
{
std::cout << divide(20, 2, 2); //should print 5
std::cin.get();
}
您对 divide
的实施基本上扩展为以下内容:
divide(20, 2, 2)
-> return 20 / divide(2,2)
-> return 20 / 1
你要么想要像这样从左到右划分:
template<class first_t, class second_t, class... rest_t>
double divide(const first_t& first, const second_t& second, const rest_t&... rest)
{
return divide(first/second, rest...);
}
或按照上面评论中的建议乘以其余的除数。
作为辅助节点,C++17 将包含一个新的 fold expression 语法,允许您这样写:
template<class... value_t>
auto divide(const value_t&... values) {
return (... / values);
// and (values / ...) would replicate your original implementation :)
}
这将自动对大小为 1 的参数包执行 The Right Thing™。
能够使用cout
或printf
了解执行流程对诊断问题很有帮助。下面是一个懒人检测代码以诊断问题的方法。
#include <iostream>
template<class first_t>
double divide(const first_t &first)
{
std::cout << "Came to 1\n";
return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
std::cout << "Came to 2\n";
auto res = divide(rest...);
std::cout << "res: " << res << "\n";
return 1.0*first / res;
}
int main()
{
std::cout << divide(20, 2, 2) << std::endl;
return 0;
}
输出:
Came to 2
Came to 2
Came to 1
res: 2
res: 1
20
正如@melak47 指出的那样,您最终在递归调用中向右折叠。要向左折叠,请评估结果并将结果转发给列表的其余元素。如果你想在没有递归的情况下进行折叠,你也可以通过 std::accumulate
.
扩展可变参数(保证按顺序排列)和 运行 值
template <typename... Values>
double divide(double dividend, Values... divisors) {
std::initializer_list<double> div_list = {double(divisors)...};
return std::accumulate(std::begin(div_list), std::end(div_list), dividend,
std::divides<>());
}
如果想避免重复除法,也可以直接乘除除法。
template <typename... Values>
double divide(double dividend, Values... divisors) {
std::initializer_list<double> div_list = {double(divisors)...};
return dividend / std::accumulate(std::begin(div_list), std::end(div_list),
1.0, std::multiplies<>());
}
我有以下小的可变模板,它应该划分一系列数字。据我了解,这是我调用 divide(20, 2, 2)
-> 20/ 2 / 2
时会发生什么的粗略描述。显然它不会发生那么好,因为我得到的答案是 20 ...当只有两个参数时它工作正常。
#include <iostream>
template<class first_t>
auto divide(const first_t &first)
{
return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
return first / divide(rest...);
}
int main()
{
std::cout << divide(20, 2, 2); //should print 5
std::cin.get();
}
您对 divide
的实施基本上扩展为以下内容:
divide(20, 2, 2)
-> return 20 / divide(2,2)
-> return 20 / 1
你要么想要像这样从左到右划分:
template<class first_t, class second_t, class... rest_t>
double divide(const first_t& first, const second_t& second, const rest_t&... rest)
{
return divide(first/second, rest...);
}
或按照上面评论中的建议乘以其余的除数。
作为辅助节点,C++17 将包含一个新的 fold expression 语法,允许您这样写:
template<class... value_t>
auto divide(const value_t&... values) {
return (... / values);
// and (values / ...) would replicate your original implementation :)
}
这将自动对大小为 1 的参数包执行 The Right Thing™。
能够使用cout
或printf
了解执行流程对诊断问题很有帮助。下面是一个懒人检测代码以诊断问题的方法。
#include <iostream>
template<class first_t>
double divide(const first_t &first)
{
std::cout << "Came to 1\n";
return first;
}
template<class first_t, class... rest_t>
double divide(const first_t &first, const rest_t&... rest)
{
std::cout << "Came to 2\n";
auto res = divide(rest...);
std::cout << "res: " << res << "\n";
return 1.0*first / res;
}
int main()
{
std::cout << divide(20, 2, 2) << std::endl;
return 0;
}
输出:
Came to 2
Came to 2
Came to 1
res: 2
res: 1
20
正如@melak47 指出的那样,您最终在递归调用中向右折叠。要向左折叠,请评估结果并将结果转发给列表的其余元素。如果你想在没有递归的情况下进行折叠,你也可以通过 std::accumulate
.
template <typename... Values>
double divide(double dividend, Values... divisors) {
std::initializer_list<double> div_list = {double(divisors)...};
return std::accumulate(std::begin(div_list), std::end(div_list), dividend,
std::divides<>());
}
如果想避免重复除法,也可以直接乘除除法。
template <typename... Values>
double divide(double dividend, Values... divisors) {
std::initializer_list<double> div_list = {double(divisors)...};
return dividend / std::accumulate(std::begin(div_list), std::end(div_list),
1.0, std::multiplies<>());
}