如何避免参数的隐式转换导致无限递归?

huangapple go评论85阅读模式
英文:

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 &lt;stdio.h&gt;
#include &lt;string&gt;

// base
std::string format(const char *s)
{
	printf(&quot;** %s\n&quot;,s);
	return &quot;&quot;;
}
// recursive, should parse each argument and print the formatted string
template&lt;typename T, typename... Args&gt;
std::string format(const char *s, T value, Args... args)
{
	printf(&quot;** %s\n&quot;,s);  // dummy
	return &quot;&quot;;
}

int main()
{
   format(&quot;foo&quot;);	
   printf(&quot;yay!\n&quot;);
}

now I'd like to pass a std::string to format, I have to do:

std::string s = &quot;foo&quot;;
format(s.c_str());

I'd like to do

format(s);

So I have added this

// recursive
template&lt;typename... Args&gt;
std::string format(const std::string &amp;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 &lt;stdio.h&gt;
#include &lt;string&gt;

// recursive
template&lt;typename... Args&gt;
std::string format(const std::string &amp;s,Args... args)
{
  return format(s.c_str(), args...);
}


// base
std::string format(const char *s)
{
	printf(&quot;** %s\n&quot;,s);
	return &quot;&quot;;
}
// recursive, should parse each argument and print the formatted string
template&lt;typename T, typename... Args&gt;
std::string format(const char *s, T value, Args... args)
{
	printf(&quot;** %s\n&quot;,s);  // dummy
	return &quot;&quot;;
}

int main()
{
   std::string s = &quot;foo&quot;;
   format(s);	
   printf(&quot;yay!\n&quot;);  // 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&lt;typename... Args&gt;
std::string format(const std::string &amp;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 &lt;stdio.h&gt;
#include &lt;string&gt;

// base
std::string format(const char *s)
{
    printf(&quot;** %s\n&quot;,s);
    return &quot;&quot;;
}

// recursive
template&lt;typename... Args&gt;
std::string format(const std::string &amp;s,Args... args)
{
    return format(s.c_str(), args...);
}

// recursive, should parse each argument and print the formatted string
template&lt;typename T, typename... Args&gt;
std::string format(const char *s, T value, Args... args)
{
    printf(&quot;** %s\n&quot;,s);  // dummy
    return &quot;&quot;;
}

int main()
{
    std::string s = &quot;foo&quot;;
    format(s);
    printf(&quot;yay!\n&quot;);  // crashes before that
}

Output:

>
&gt; ** foo
&gt; yay!
&gt;

huangapple
  • 本文由 发表于 2023年6月16日 16:08:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/76488178.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定