提问人:jyelon 提问时间:4/8/2023 更新时间:4/8/2023 访问量:77
C++ 函数 strprint(expr, expr, expr...) 几乎可以工作,但不完全是,为什么?
C++ function strprint(expr, expr, expr...) almost works, but not quite, why?
问:
这是一个小的C++“strprint”函数,似乎大部分工作:
#include <sstream>
#include <iostream>
// send_to_stream: send all arguments to the specified stream.
inline void send_to_stream(std::ostream &os) {}
template <typename ARG, typename... REST>
inline void send_to_stream(std::ostream &os, const ARG &arg, const REST & ... rest) {
os << arg;
send_to_stream(os, rest...);
}
// strprint: convert all arguments to a string by sending them to a stringstream.
template <typename... ARGS>
std::string strprint(const ARGS & ... args) {
std::ostringstream oss;
send_to_stream(oss, args...);
return oss.str();
}
int main(int argc, char **argv) {
std::string s1 = strprint("five:", 5, "\n");
std::cout << "S1=" << s1 << "." << std::endl;
// std::string s2 = strprint("five:", 5, std::endl);
// std::cout << "S2=" << s1 << "." << std::endl;
};
此程序在运行时将产生输出:
five:5
.
这正是我所期望的。但是当我取消注释“s2”行时,我收到编译错误:
foo.cpp: In function ‘int main(int, char**)’:
foo.cpp:25:53: error: too many arguments to function ‘std::string strprint(const ARGS& ...) [with ARGS = {}; std::string = std::__cxx11::basic_string<char>]’
25 | std::string s2 = strprint("five:", 5, std::endl);
| ^
foo.cpp:15:13: note: declared here
15 | std::string strprint(const ARGS & ... args) {
| ^~~~~~~~
这毫无意义。一个需要无限数量参数的函数的参数太多?它似乎不喜欢通过变量列表传递“std::endl”。 为什么不呢?我错过了什么?
答:
2赞
463035818_is_not_an_ai
4/8/2023
#1
std::endl
是一个函数模板:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
诚然,错误消息相当令人困惑。问题是无法推断出 of 的类型,因为您需要先实例化函数:std::endl
std::string s2 = strprint("five:", 5, std::endl<std::ostream::char_type,std::ostream::traits_type>);
std::cout << "S2=" << s1 << "." << std::endl;
通常
std::cout << std::endl;
由于 https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt 这里的过载 20 而工作。通常,所有 io 操纵器都是以流为参数的函数。并且 generall 所有仅为输入或输出流定义的 io 操纵器分别是函数模板。
当然,您不想显式提供 和 .正如 Remy Lebeau 所指出的,您可以使用(非捕获)lambda:char_type
traits_type
auto endl = [](std::ostream os&){ os << std::endl; };
std::string s2 = strprint("five:", 5, endl);
这将调用重载 18(来自 avove 链接)。
评论
0赞
jyelon
4/8/2023
谢谢!这是有道理的。那么,运算符<<如何接受函数模板作为右侧的参数呢?有什么方法可以我自己做同样的魔术吗?
0赞
463035818_is_not_an_ai
4/8/2023
@jyelon en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt 18
1赞
Remy Lebeau
4/8/2023
实际上,@463035818_is_not_a_number会使用重载 #20 而不是 #18,因为它需要/返回 a 而不是 a 或std::endl
basic_ostream&
ios_base&
basic_ios&
0赞
Remy Lebeau
4/8/2023
@jyelon有一个重载,它接受匹配 的 签名的 C 样式函数指针。编译器可以推断出要实例化的类型,因为运算符的参数使用被打印到的模板参数。你的ostream::operator<<
std::endl
std::endl
ostream
strprint()
0赞
Remy Lebeau
4/8/2023
@463035818_is_not_a_number不太详细的解决方案是使用 lambda 而不是显式实例化,例如:demoauto endl = [](std::ostream os&){ os << std::endl; }; std::string s2 = strprint("five:", 5, endl);
评论
std::endl
std::endl
,作为函数模板,没有类型。所以不能正确推断,最终被推导出为空列表。ARGS...
template <typename ARG, typename... REST> inline void send_to_stream(std::ostream &os, ARG &&arg, REST &&... rest) { os << std::forward<ARG>(arg); send_to_stream(os, std::forward<REST>(rest)...); }
template <typename... ARGS> std::string strprint(ARGS && ... args) { ... send_to_stream(oss, std::forward<ARGS>(args)...); ... }