可变参数模板与使用元组在参数中添加不同的数据对

variadic templates vs using tuples for adding different data pairs in an argument

本文关键字:参数 添加 数据 元组 变参      更新时间:2023-10-16

以下主代码工作正常

string hello = "Hello ";
string world = "templates!";
cout << "var template add: ";

cout << setprecision(2) <<
var_template_add(5, 4, 5.5, 4.0);

用于以下模板生成器代码和可变参数函数

template <typename T> 
auto add(T a, T b) {
return a + b;
}

template <typename T, typename... Rest> 
auto var_template_add(T first, T second, Rest... others) {
return first + second + add (others...);
}

但是如果再添加两个字符串参数,就像这样

var_template_add(5, 4, 5.5, 4.0, hello, world);

被编译器错误捕获,指出"未找到匹配的重载函数"。

string hello = "Hello ";
string world = "templates!";
cout << "strings add result: " << setprecision(2) << add(hello, world) << endl;

如果我像下面这样编写模板函数"添加",则可以工作:

template <typename T1, typename T2>
auto add(T1 a, T2 b) {
return a + b;
}

我的问题是,我怎么能做线

var_template_add(5, 4, 5.5, 4.0, hello, world);

无需像上面那样编写另一个 Add 函数即可工作?!

请注意,我可以使用元组来传递此值,但现在,我只想远离。 有什么想法/改进吗?

std::string添加到整数值将不起作用,除非您选择使函数显式转换为字符串,例如使用std::stringstreamstd::to_string

如果您希望它通过字符串化行为正确运行,则需要更改它以执行某种形式的显式字符串构建。

但是,您至少可以使当前var_template_add使用任意数量的参数,因为当前定义需要 4 个参数,否则将无法工作。这将允许var_template_add(hello, world)示例工作。


在 C++11 中,这可以通过使用一些模板递归来完成。这本身不需要添加任何新功能 - 只需重命名现有函数并更改一个即可。

这个想法是递归调用var_template_add,直到你最终得到 2 个参数,然后将两者加在一起:

// rename'add' to 'var_template_add'. Use this as recursive base-case.
template <typename T, typename U> 
auto var_template_add(T first, U second) 
-> decltype(first + second)
{
return first + second;
}
// adds first argument and delegates 'second' and 'others...' to the next 'var_template_add'
template <typename T, typename U, typename... Rest> 
auto var_template_add(T first, U second, Rest... others) 
-> decltype(first + var_template_add(second, others...))
{
return first + var_template_add(second, others...);
}

对于 2 个参数,它将调用第一个重载。对于 3 个或更多参数,它将调用第二个参数,第二个参数将递归调用下一个var_template_add,直到最终调用第一个。

注意:此答案是因为问题被标记为 C++11 -- 但请注意,您使用没有尾随返回类型的auto返回类型实际上是 C++14 而不是 C++11。


如果您有 C++17,您可以使用可变参数折叠表达式更轻松地执行此操作:

template <typename T, typename U, typename...Rest>
auto var_template_add(T first, U second, Rest...others)
{
return first + second + (... + others);
}
>编辑:

由于 OP 更新了标签以包含c++14c++17,因此您可以使用to_stringstringstream和折叠表达式非常轻松地实现字符串化添加行为。为此,我确实推荐了一个不同的函数——因为附加字符串序列在语义上与"添加"值完全不同。

最好的方法可能是使用std::stringstream,例如:

template <typename...Args>
std::string variadic_add_str(Args&&...args)
{
auto stream = std::string_stream{};
stream << (... << std::forward<Args>(args));
return stream.str(); 
}

当前调用工作的唯一方法是让var_template_add返回单个类型,因此它需要是一个字符串。

相反,您可以在函数中编写cout,因此您只需要:

template <typename T, typename... Rest> 
auto var_template_add(T first, T second, Rest... others) {
cout << setprecision(2) << (first + second) << " ";
if constexpr (sizeof...(others)) var_template_add(others...);
}

这是一个演示。

请注意,在 c++17 之前没有if constexpr,因此在这种情况下,将额外的重载作为基本情况是一个不错的选择。