在没有格式说明符的情况下在c++中使用类似printf的实用程序



我正在尝试编写一个函数,该函数可以将其参数转换为字符串。然而,我发现很难打开参数包。

这是我写的代码:

#include <iostream>
#include <sstream>
template <typename... T>
std::string StringFormatter(T... values)
{
std::ostringstream out;
for (auto&& x : { values... }) {
out << x;
}
return out.str();
}
int main()
{
auto&& i = StringFormatter("One ", "two");  //Success
auto&& j = StringFormatter("one ", 1, "two", 2.0); //Fails
std::cout << i;
}

我知道上面的代码失败了,因为初始值设定项列表只接受单个类型的参数。

我尝试了一种递归方法来实现上述实现,但没有成功。

如果你能提出一个更好的方法来实现这一点,那将是一个很大的帮助。

您可以使用C++17的折叠表达式来实现这一点

template <typename... T>
std::string StringFormatter(T... values)
{
std::ostringstream out;
(out << ... << values);
return out.str();
}

简而言之:

如果你没有C++17编译器,你可以依靠int数组技巧:

template <typename... T>
std::string StringFormatter(T... values) {
std::ostringstream out;
int arr[] = { 0, (out << values, void(), 0)... };
return out.str();
}

在参数包为空的情况下,因为无法实例化大小为0的数组,所以需要在数组开头使用明显无用的0void()在那里是为了规避假设的operator,过载。

评估顺序是有保证的,编译器应该能够优化结果二进制文件中的数组。

深度:

这种技术是C++17之前的折叠表达式。基本上,我们创建一个sizeof...(T) + 1元素的数组(全部为0(。这里的问题是,我们使用,运算符的属性来对参数包的每个元素运行我们想要的操作。

让我们暂时忘记参数包和模板。当你这样做:

something, other_thing

假设,运算符没有重载,则该语句被求值为other_thing。但这并不意味着something被忽略。它的值只是被丢弃以支持other_thing。我们正在利用这个特性来耍小把戏。

int x = 0;
int a[] = { 0, (++x, 0) }; // a is {0, 0}, x is 1

现在,由于您可以重载operator,,我们只需添加一个额外的语句来避免这种假设的重载:

(something, void(), 0)

由于operator,是一个二进制运算符,因此它的重载版本不能只有一个参数。通过向void添加一个语句求值,我们可以防止任何假设的过载被选取,因此我们确信最终会得到0

最后一步是将其与我们的参数包相结合,并对生成的语句执行包扩展:

(out << values, void(), 0)...

现在有更好的方法(使用fold表达式(,但如果你想使用递归方法,它可以看起来像这样:

#include <sstream>
#include <string>
#include <iostream>
template <class T>
std::string stringify(T const &t) { 
std::stringstream b;
b << t;
return b.str();
}
template<typename T, typename... Args>
std::string stringify(T arg, const Args&... args) {
return stringify(arg) + stringify(args...);
}
int main() {
std::string three{" three"};
std::cout << stringify("one: ", 1, " two: ", 2, three, "n");
return 0;
}

您应该能够将其用于基本上任何支持流插入的类型。如果你传递了足够多的参数,以至于参数数量的二次时间是一个问题,1(去看心理医生,2(可以按照这个一般顺序使用更多的代码:

#include <sstream>
#include <string>
#include <iostream>
namespace detail {
template <class T>
void stringify(std::ostringstream &b, T const &t) {
b << t;
}
template<typename T, typename... Args>
void stringify(std::ostringstream &os, T arg, const Args&... args) {
stringify(os, arg);
stringify(os, args...);
}
}
template <typename ...Args>
std::string stringify(const Args &...args) {
std::ostringstream os;
detail::stringify(os, args...);
return os.str();
}
int main() {
std::string three{" three"};
std::cout << stringify("one: ", 1, " two: ", 2, three, "n");
}

但一定要先去看心理医生。如果你传递了足够多的论据来证明这一点,那么你显然做错了什么。

相关内容

  • 没有找到相关文章

最新更新