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

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::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 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++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(); 
}