可变参数模板与使用元组在参数中添加不同的数据对
variadic templates vs using tuples for adding different data pairs in an argument
以下主要代码工作正常
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);
无需像上面那样编写另一个添加函数就可以工作?!
请注意我可以使用元组来传递这个值,但现在,我只想远离。任何 thoughts/improvements?
当前调用工作的唯一方法是 var_template_add
到 return 单一类型,因此它需要是一个字符串。
相反,您可以在函数内部编写 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...);
}
这里是 demo。
请注意,没有 if constexpr
pre c++17,因此在这种情况下,将额外的重载作为基本情况是一个不错的选择。
将 std::string
添加到整数值将不起作用,除非您选择让函数显式转换为字符串,例如使用 std::stringstream
或 std::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
return 类型没有尾随 return 类型实际上是 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++14
和 c++17
,您可以使用 to_string
或 stringstream
和折叠表达式很容易地实现字符串化添加行为.为此,我确实推荐了一个不同的函数——因为附加字符串序列在语义上与“添加”值的操作完全不同。
最好的方法可能是使用 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();
}
以下主要代码工作正常
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);
无需像上面那样编写另一个添加函数就可以工作?!
请注意我可以使用元组来传递这个值,但现在,我只想远离。任何 thoughts/improvements?
当前调用工作的唯一方法是 var_template_add
到 return 单一类型,因此它需要是一个字符串。
相反,您可以在函数内部编写 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...);
}
这里是 demo。
请注意,没有 if constexpr
pre c++17,因此在这种情况下,将额外的重载作为基本情况是一个不错的选择。
将 std::string
添加到整数值将不起作用,除非您选择让函数显式转换为字符串,例如使用 std::stringstream
或 std::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
return 类型没有尾随 return 类型实际上是 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++14
和 c++17
,您可以使用 to_string
或 stringstream
和折叠表达式很容易地实现字符串化添加行为.为此,我确实推荐了一个不同的函数——因为附加字符串序列在语义上与“添加”值的操作完全不同。
最好的方法可能是使用 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();
}