【问题标题】:parameter packs parameter type参数包参数类型
【发布时间】:2021-07-31 08:46:45
【问题描述】:

我已经修改了https://en.cppreference.com/w/cpp/language/parameter_pack 中的示例,以将字符串保存在变量中。我的代码

#include <string>

void tprintf(std::string& str, const std::string& format) 
{
    str += format; 
}

template <typename T, typename... Targs>
void tprintf(std::string& str, const std::string& format, T arg, Targs ...Fargs)
{
    for ( int i = 0; i < format.size(); i++ )
    {
        if ( format.at(i) == '%' )
        {
            if (format.at(i + 1) == 'd') 
            {
                std::cout << "== 'd' -variable = " << arg << std::endl;
                str += std::to_string(arg);
            }
            else if (format.at(i + 1) == 's') 
            {
                std::cout << "== 's'" << std::endl;
                str += arg;
            }
            tprintf(str, (i + 2 < format.size() ? format.substr(i + 2) : ""), Fargs...);
            break;  
        }
        str += format.at(i);
    }
}

int main()
{
    std::string str;
    int age = 24;
    std::string name("Hugo");
    tprintf(str, "Name: %s, age: %d years\n", name, age);
    std::cout << "result = " << str << std::endl;
}

编译代码时出现以下错误:

error: no matching function for call to ‘to_string(std::__cxx11::basic_string<char>&)’
                 str += std::to_string(arg);

我认为参数包扩展为

  1. tprintf(std::string&amp;, const std::string&amp;, std::string, int) arg 是 std::string
  2. tprintf(std::string&amp;, const std::string&amp;, int) arg 是 int 我可以打电话给 std::to_string(arg) 但似乎arg 的类型不同于intstd::string 或其他类型)? 当我删除 std::to_string 调用时,我得到了一些神秘字符。

如何将年龄值转换为string 并将其附加到str

【问题讨论】:

  • name 是传递给argstd::string,但to_string 中的std::string 参数没有重载,因为它已经是一个字符串。虽然 Ngl,但我有时确实希望 std::string 有一个重载,它什么都不做,并在这种情况下返回一个字符串。
  • @mediocrevegetable1 to_string还有其他缺点,就是要和std::sprintf一样转换,也没有sprintf可以从std::strings转换跨度>
  • @mediocrevegetable1 在第一次递归调用 tprintf 之后,我为 int 变量 (age) 调用 to_string。如果我理解正确,函数调用看起来像这样 tprintf(std::string& str, const std::string& format, int arg)。
  • 这并不意味着调用to_string 的代码不会被编译两次。即使格式说明符不是%d,它仍然必须编译to_string(arg),这在它是字符串时会导致错误。
  • 离题:at 实现了一个额外的范围检查,在像你这样基于向量范围的循环中完全过时了。在这种情况下,您应该首选operator[]

标签: c++ variadic-functions parameter-pack


【解决方案1】:

tprintf(std::string&, const std::string&, std::string, int)

因此,这里是正确的:

 str += std::to_string(arg);

argstd::string,不存在std::to_string 这样的重载。

无论T 是什么类型,生成的模板都必须是有效的C++ 代码。即使对应的格式化字符是d,函数中的所有内容仍然必须是有效的C++代码。

一般来说,尝试在类型安全的 C++ 中实现 C 风格的 printf 格式并没有多大意义。仅仅因为您知道相应参数的类型,您就已经知道需要格式化什么。

您在 C 风格的 printf 字符串中使用不同的格式说明符(例如 %s%d)的唯一原因是因为这个 C 函数不知道传递给它的内容是什么,如一个参数,所以它依赖于实际的格式说明符来知道它是什么。

这显然不是 C++ 的情况。您的格式说明符本身可以只是一个%,没有别的。您确切地知道传入了什么参数。因此,仅定义三个重载函数要简单得多:

template <typename... Targs>
void tprintf(std::string& str, const std::string& format, int arg, Targs ...args)
{
   // Just the code that formats an int
}

template <typename... Targs>
void tprintf(std::string& str, const std::string& format, const std::string &arg, Targs ...args)
{
   // Just the code that formats a std::string
}

void tprintf(std::string& str, const std::string& format)
{
   // Nothing more to format, just adds everything else in "format" to "str".
}

在前两种情况下,只需在format 中搜索第一个% 占位符,将其之前的所有内容复制到str,然后是格式化参数,然后然后递归地用剩余的args重新调用tprintf,以及format中剩下的内容。

附:使用转发引用,“Targs && ...args”,连同std::forward,将是一个后续调整。

【讨论】:

    【解决方案2】:

    if 语句的所有分支都被编译。编译器正在尝试将字符串参数传递给字符串,并且不存在重载。

    它不会在运行时发生的事实是无关紧要的。在编译时,编译器需要知道什么

    tprintf(str, "Name: %s, age: %d years\n", name, age);
    

    做了做什么

    tprintf(str, "Name: %d, age: %d years\n", name, age);
    

    会的。

    考虑两次不包括类型信息。你已经知道name是一个字符串,age是一个整数,在格式字符串中重复是没有意义的。

    我建议在格式字符串中使用位置命令,因为这也使本地化更加实用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-04
      • 2017-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多