英文:
how to avoid implicit conversion of arguments leading to infinite recursion?
问题
我有一个格式化方法,我从这个示例中适应过来。
我已经将它简化为只有函数调用和打印操作。
当格式化字符串(第一个参数)是const char *
时,它可以正常工作。
#include <stdio.h>
#include <string>
// 基本版本
std::string format(const char *s)
{
printf("** %s\n", s);
return "";
}
// 递归版本,应该解析每个参数并打印格式化后的字符串
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n", s); // 假设
return "";
}
int main()
{
format("foo");
printf("yay!\n");
}
现在,我想要将std::string
传递给format
,我需要这样做:
std::string s = "foo";
format(s.c_str());
我想要这样做:
format(s);
因此,我添加了这个函数:
// 递归版本
template<typename... Args>
std::string format(const std::string &s, Args... args)
{
return format(s.c_str(), args...);
}
但是,当我直接将std::string
作为参数传递给它时,程序崩溃了。调试显示了无限递归。在使用std::string
构造函数调试模板时可能会很困难,但我的猜测是这个问题出在这里:
return format(s.c_str(), args...);
它会无限递归调用自身,因为const char *
会隐式转换为std::string
。
以下是完整的不工作示例:
#include <stdio.h>
#include <string>
// 递归版本
template<typename... Args>
std::string format(const std::string &s, Args... args)
{
return format(s.c_str(), args...);
}
// 基本版本
std::string format(const char *s)
{
printf("** %s\n", s);
return "";
}
// 递归版本,应该解析每个参数并打印格式化后的字符串
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n", s); // 假设
return "";
}
int main()
{
std::string s = "foo";
format(s);
printf("yay!\n"); // 在此之前崩溃
}
我可以完全放弃const char *
版本,而只使用std::string
,但是我想避免在传递字符串文字时构建一个字符串。
那么,如何保留直接传递const char *
或std::string
作为第一个参数的能力?
英文:
I have a formatting method that I adapted from this example.
I have reduced it to just function calls and a print
It works when the formatting string (first argument) is a const char *
#include <stdio.h>
#include <string>
// base
std::string format(const char *s)
{
printf("** %s\n",s);
return "";
}
// recursive, should parse each argument and print the formatted string
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n",s); // dummy
return "";
}
int main()
{
format("foo");
printf("yay!\n");
}
now I'd like to pass a std::string
to format
, I have to do:
std::string s = "foo";
format(s.c_str());
I'd like to do
format(s);
So I have added this
// recursive
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
return format(s.c_str(), args...);
}
But when I pass the string directly as std::string
it crashes. Debugging shows an infinite recursion. Difficult to debug templates with std::string
constructions, but my guess is that
return format(s.c_str(), args...);
calls itself, because const char *
implicitly converts as a std::string
.
Here's the full non-working example:
#include <stdio.h>
#include <string>
// recursive
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
return format(s.c_str(), args...);
}
// base
std::string format(const char *s)
{
printf("** %s\n",s);
return "";
}
// recursive, should parse each argument and print the formatted string
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n",s); // dummy
return "";
}
int main()
{
std::string s = "foo";
format(s);
printf("yay!\n"); // crashes before that
}
I could ditch const char *
version altogether and go full std::string
but I would like to avoid to build a string when a string literal is passed.
So how to keep the ability to pass either const char *
directly or std::string
as first argument ?
答案1
得分: 1
您的递归版本在其return
语句中没有看到任何可委派的声明。
这是您在代码中拥有的最顶层声明和定义:
template<typename... Args>
std::string format(const std::string &s, Args... args)
{
return format(s.c_str(), args...);
}
它不会“看到”任何其他候选项,因此它将始终递归地调用自身。
您可以通过在上面引入一个基本情况的声明来修复它。或者将该情况移动到那里,并包括其实现:
#include <stdio.h>
#include <string>
// 基本情况
std::string format(const char *s)
{
printf("** %s\n", s);
return "";
}
// 递归
template<typename... Args>
std::string format(const std::string &s, Args... args)
{
return format(s.c_str(), args...);
}
// 递归,应解析每个参数并打印格式化后的字符串
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n", s); // 假设
return "";
}
int main()
{
std::string s = "foo";
format(s);
printf("yay!\n"); // 在此之前崩溃
}
输出:
** foo yay!
英文:
Your recursive version does not see any declarations to which it could delegate in its return
statement.
This is the uppermost declaration and definition you have in your code:
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
return format(s.c_str(), args...);
}
It doesn't "see" any other candidates, thus it will always call itself recursively.
You can fix it by introducing a declaration to your base case above it. Or moving said case there including its implementation:
#include <stdio.h>
#include <string>
// base
std::string format(const char *s)
{
printf("** %s\n",s);
return "";
}
// recursive
template<typename... Args>
std::string format(const std::string &s,Args... args)
{
return format(s.c_str(), args...);
}
// recursive, should parse each argument and print the formatted string
template<typename T, typename... Args>
std::string format(const char *s, T value, Args... args)
{
printf("** %s\n",s); // dummy
return "";
}
int main()
{
std::string s = "foo";
format(s);
printf("yay!\n"); // crashes before that
}
Output:
>
> ** foo
> yay!
>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论