Ed *_*hne 9 c++ lambda iostream manipulators c++11
我正在尝试使用C++ 11功能来使自定义流操纵器更容易创建.我可以使用lambda函数作为操纵符,但不是std::function<ostream&(ostream&)>.
这是代码,归结为:
#include <iostream>
#include <functional>
using namespace std;
auto lambdaManip = [] (ostream& stream) -> ostream& {
stream << "Hello world" << endl;
};
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
stream << "Hello world" << endl;
};
int main (int argc, char** argv) {
cout << lambdaManip; // OK
cout << functionManip; // Compiler error
}
Run Code Online (Sandbox Code Playgroud)
第二个cout语句失败,并带有以下内容:
g++-4 src/Solve.cpp -c -g -std=c++0x -o src/Solve.o -I/home/ekrohne/minisat
src/Solve.cpp: In function 'int main(int, char**)':
src/Solve.cpp:24:11: error: cannot bind 'std::ostream' lvalue to 'std::basic_ostream<char>&&'
/usr/lib/gcc/i686-pc-cygwin/4.5.3/include/c++/ostream:579:5: error: initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char, _Traits = std::char_traits<char>, _Tp = std::function<std::basic_ostream<char>&(std::basic_ostream<char>&)>]'
Run Code Online (Sandbox Code Playgroud)
为什么这会失败?我正在使用cygwin gcc 4.5.3.
虽然我在问,但std::function由于效率问题,我并不担心随处可见.但我确实希望编写返回lambda函数的函数,并且不知道如何在没有函数的情况下执行此操作std::function.例如,像下面这样的东西会很棒
auto getAdditionFunctor();
auto getAdditionFunctor() {
return [] (int x, int y) { return x + y };
};
Run Code Online (Sandbox Code Playgroud)
......但显然不起作用.是否有替代语法可行?我无法想象它会是什么,所以我可能会被困住std::function.
如果我对第二个问题有解决方案,那么第一个问题就没有实际意义了.
谢谢.
定义有operator<<(ostream&, std::function<ostream&(ostream&)>帮助.我误读了一个网页,并且印象ostream非常聪明,可以处理一个operator()具有操纵器的任意对象.我错了.此外,lambda正如我被告知的那样,我构建的简单可能只是编译成一个普通的旧函数.实际上,如果我使用变量捕获来确保它lambda不是一个简单的函数,那么编译器就会失败.此外,operator()定义的对象(默认情况下)不被视为操纵器:
class Manipulator {
ostream& operator()(ostream& stream) const {
return stream << "Hello world" << endl;
};
} classManip;
function<ostream& (ostream&)> functionManip = [] (ostream& stream) -> ostream& {
return stream << "Hello world" << endl;
};
int main (int argc, char** argv) {
const string str = "Hello world";
auto lambdaManip = [&] (ostream& stream) -> ostream& {
return stream << str << endl;
};
cout << classManip; // Compiler error
cout << lambdaManip; // Compiler error
cout << functionManip; // Compiler error
}
Run Code Online (Sandbox Code Playgroud)
进一步更新:结果表明,下面的解决方案可以通过以下方式实现:
// Tell ostreams to interpret std::function as a
// manipulator, wherever it sees one.
inline ostream& operator<<(
ostream& stream,
const function<ostream& (ostream&)>& manipulator) {
return manipulator( stream );
}
Run Code Online (Sandbox Code Playgroud)
此代码有一个额外的const.我发现这试图在我的项目中实际实现解决方案.
如果你看operator<<的ostream,有没有过载走std::function-那就是基本上你想在这里用做什么cout << functionManip.要解决此问题,请自行定义重载:
ostream& operator<<(ostream& os, std::function<ostream& (ostream&)>& s)
{
return s(os);
}
Run Code Online (Sandbox Code Playgroud)
或者将stream作为参数传递给函数:
functionManip(std::cout);
Run Code Online (Sandbox Code Playgroud)
至于为什么lambda工作正常,因为a的返回类型lambda是未定义的,并且使用函数指针存在重载:
ostream& operator<< (ostream& ( *pf )(ostream&));
Run Code Online (Sandbox Code Playgroud)
lambda可能利用aa结构包装所有内容,并定义operator()在这种情况下哪个结构与函数指针完全相同.这对我来说是最可能的解释,希望如果我错了,有人可以纠正我.
这不是错误的原因,但由于你已经定义了两者lambdaManip并且functionManip具有返回类型,ostream&我相信你已经忘记添加return stream;两者.
调用cout << functionManip失败,因为没有operator<<(ostream&, std::function<ostream&(ostream&)>定义.添加一个,呼叫将成功.
ostream& operator<<(ostream& stream, function<ostream& (ostream&)>& func) {
return func( stream );
}
Run Code Online (Sandbox Code Playgroud)
另外,您也可以拨打functionManip为
functionManip( cout );
Run Code Online (Sandbox Code Playgroud)
这将在不添加operator<<定义的情况下工作.
关于返回lambda的问题,由于返回的lambda getAdditionFunctor是一个无捕获的lambda,它可以隐式转换为函数指针.
typedef int(*addition_ptr)(int,int);
addition_ptr getAdditionFunctor()
{
return [] (int x, int y) -> int { return x + y; };
}
auto adder = getAdditionFunctor();
adder(10,20); // Outputs 30
Run Code Online (Sandbox Code Playgroud)
前面的答案得到了最先进的答案,但是如果您打算在代码中大量使用std::functions 和/或 lambda 作为流操纵器,那么您可能只想将以下函数模板定义添加到您的代码库中,在全局范围:
template<class M>
auto operator<< (std::ostream& os, const M& m) -> decltype(m(os))
{
return m(os);
}
Run Code Online (Sandbox Code Playgroud)
那里的尾随返回类型使用表达式 SFINAE,因此这个特定的operator<<重载甚至不会参与重载解析,除非m(os)是格式良好的表达式。(这就是你想要的。)
然后你甚至可以做类似的事情
template<class T>
auto commaize(const T& t)
{
return [&t](std::ostream& os) -> std::ostream& {
return os << t << ", ";
};
}
int main()
{
std::cout << commaize("hello") << commaize("darling") << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
(请注意,上面的代码中完全没有任何对象!除非绝对必要,否则std::function请尽量避免将 lambda 表达式填充到对象中,因为构造对象的成本可能很高;它甚至可能涉及内存分配。)std::functionstd::function
对于 C++17 来说,将此重载添加到标准库是有意义的operator<<,但目前我不知道该领域有任何具体建议。