参数包参数类型

parameter packs parameter type

我修改了 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&, const std::string&, std::string, int) <- argstd::string
  2. tprintf(std::string&, const std::string&, int) <- argint 我可以调用 std::to_string(arg) 但是 arg 似乎有不同于 int 的另一种类型(std::string 或其他)? 当我删除 std::to_string 调用时,我得到了一些神秘字符。

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

tprintf(std::string&, const std::string&, std::string, int) <- arg is std::string

正确,因此,这里:

 str += std::to_string(arg);

arg 是一个 std::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 ,然后是格式化参数,然后递归地重新调用 tprintf 剩余的 args,以及 format.

中剩下的内容

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

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

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

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

做什么

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

会做。

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

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