C++ 11 已经原生支持了 lambda 表达式。如果真的需要标题中的写法,请考虑使用原生的 lambda 表达式。
虽然说在 for_each(begin, end, functor) 中,functor 不能使用
std::cout << boost::lambda::_1 << std::endl
的写法,但是毫无疑问,使用
std::cout << boost::lambda::_1 << "\n"
是没有问题的。
这个初看起来可能很奇怪。实际上原因是这样的。
在我的 MSVC++ 2012 RC 的 STL 中,std::endl 的定义是这样的(经过简化):
ostream& endl(ostream& os) {
os.put('\n');
os.flush();
return os;
}
wostream& endl(wostream& os) {
os.put('\n');
os.flush();
return os;
}
请注意这儿 endl 是被重载了的(实际上还有更多重载版本,这儿只列出来两个,可以说明问题即可。)。
而 std::basic_ostream (std::ostream / std::wostream 实际上是对 std::basic_ostream 实例化的结果)中对 operator << 有这么一个重载版本(经过简化):
basic_ostream<x, y> operator << (basic_ostream<x, y>& (__cdecl *func)(basic_ostream<x, y>&)) {
return (func(*this));
}
这样,当碰到
std::cout << std::endl;
就根据 std::cout 的类型把 wchar_t 等等的 std::endl 否定掉,这样就只有 char 类型的 std::endl 可选。
于是调用了 std::endl 这个函数,然后输出换行并刷新缓冲区。
而到了
std::cout << boost::lambda::_1 << std::endl;
这儿,情况就完全不一样了。
首先,为了能够让
std::cout << boost::lambda::_1
生成一个 functor 来让 for_each 接受它,boost 库有类似这样的重载(经过简化):
lambda_functor<...> operator << (ostream& os, lambda_functor<...>& arg) {
...
}
其中 lambda_functor 正是 boost::lambda::_1 的类型。
这个重载版本实际上是返回了一个 lambda_functor_base 类型的对象,因此
std::cout << boost::lambda::_1;
实际上会构造一个 lambda_functor_base 类型的对象传给 for_each,这样如下的代码就可以编译通过了:
std::for_each(begin, end, std::cout << boost::lambda::_1);
那么 boost::lambda 中那个重载版本的 operator << 究竟构造成了什么样的对象呢?是这样的:
boost::lambda::lambda_functor<
boost::lambda::lambda_functor_base<
boost::lambda::bitwise_action<
boost::lambda::leftshift_action
>,
boost::tuples::tuple<
std::basic_ostream<char, std::char_traits<char>> &,
boost::lambda::lambda_functor<boost::lambda::placeholder<1>>,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type,
boost::tuples::null_type
>
>
>
很遗憾,它没有针对 ostream 所使用的 std::endl 的类型,也就是:
std::basic_ostream<char, std::char_traits<char>>& (endl_type)(std::basic_ostream<char, std::char_traits<char>>&)
专门重载 operator <<,所以编译器就无从得知该用哪个 std::endl,进而产生了编译错误。
当然,如果我们告诉编译器我们需要用的 std::endl 是哪个版本,那么也就不会出现问题了。就像这样(我这儿的 ostream 所使用的 std::endl 是 std::ostream& (*)(std::ostream&) 类型的):
std::for_each(begin, end, std::cout << boost::lambda::_1 << (std::ostream& (*)(std::ostream&))std::endl);
如果从技术上,单独针对
std::cout << boost::lambda::_1 << std::endl
这样的语句做一个重载版本,我觉得应该是可行的,但是一方面,在实际使用中还有类似于
std::cout << boost_lambda::_1 << 1 << ... << std::endl
这种语句,另外一方面据说 GCC 下面 std::endl 是作为一个模板函数实现的,所以要把所有情况全部处理掉可能就会存在问题。
另外,如何把 ostream 使用的 std::endl 跟 wostream 使用的 std::endl 区分开也是一个问题。
当然,现在 C++ 11 有了原生的 lambda 支持,这么写就可以了:
std::for_each(begin, end, [] (type value) {
std::cout << value << std::endl;
}