1

我已经修改了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有另一种类型而不是intstd::string或其他)?当我删除std::to_string呼叫时,我得到了一些神秘的字符。

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

4

2 回答 2

2

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

因此,这里是正确的:

 str += std::to_string(arg);

arg是 a std::string,并且没有这样的std::to_string重载。

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

通常,尝试printf在类型安全的 C++ 中实现 C 风格的格式化没有多大意义。由于您知道相应参数的类型,因此您已经知道需要格式化什么。

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

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

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.

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

于 2021-05-09T20:57:34.553 回答
1

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

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

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

做什么做什么

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

会做。

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

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

于 2021-05-09T21:00:46.267 回答