参数包参数类型
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);
我以为参数包扩展到
tprintf(std::string&, const std::string&, std::string, int)
<- arg
是 std::string
tprintf(std::string&, const std::string&, int)
<- arg
是 int
我可以调用 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 是一个整数,在格式字符串中重复它是没有意义的。
我建议在格式字符串中使用位置命令,因为这也使本地化更加实用。
我修改了 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);
我以为参数包扩展到
tprintf(std::string&, const std::string&, std::string, int)
<-arg
是std::string
tprintf(std::string&, const std::string&, int)
<-arg
是int
我可以调用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 是一个整数,在格式字符串中重复它是没有意义的。
我建议在格式字符串中使用位置命令,因为这也使本地化更加实用。