c++ 中 lambda 的类型和大小

本文说明 c++11 lambda 生成一个匿名类,当没有 capture 到任何变量的时候, 大小几乎为零。其实是 1 byte ,因为 c++ 不允许 0 字节的结构体。

#include <iostream>
#include <functional>
#include <boost/type_index.hpp>
using namespace std;

template<typename T>
void show_type_and_size(T& x) {
    cout << "T = "
         << boost::typeindex::type_id_with_cvr<T>().pretty_name() << ";\n"
         << boost::typeindex::type_id_with_cvr<decltype(x)>().pretty_name() << " x;\n"
         << "sizeof(x) "  << sizeof(x) << "\n"
         << endl;
}

int main(int argc, char *argv[])
{
    auto x = []() { cout << "hello world 1" << endl; };
    auto y = []() { cout << "hello world 2" << endl; };
    show_type_and_size(x);
    show_type_and_size(y);

    function<void(void)> fx = x;
    show_type_and_size(fx);

    return 0;
}

程序的输出结果

T = main::$_0;
main::$_0& x;
sizeof(x) 1

T = main::$_1;
main::$_1& x;
sizeof(x) 1

T = std::__1::function<void ()>;
std::__1::function<void ()>& x;
sizeof(x) 48

这里可以看到,每一个 lambda 有自己的匿名类,main::$_0

如果我们试图创建这样的对象

    decltype(x) x1;

会有报错

cpp_src/cpp_lambda_size_type.cpp:21:17: error: no matching constructor for initialization of 'decltype(x)' (aka '(lambda at cpp_src/cpp_lambda_size_type.cpp:16:14)')
    decltype(x) x1;
                ^
cpp_src/cpp_lambda_size_type.cpp:16:14: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
    auto x = []() { cout << "hello world" << endl; };
             ^
cpp_src/cpp_lambda_size_type.cpp:16:14: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were provided
1 error generated.

这说明这个匿名类是没有默认构造函数的,但是可以创建引用

    decltype(x) & x1 = x;
    x1();
    decltype(y) & y1 = y;
    y1();

而把 lambda 表达式赋值给一个 function 对象的时候,这个对象的大小就是 48 bytes 了。这是因为调用了

template< class F >
function::function( F f );

这个构造函数。

从这个角度上看,直接使用 lambda ,并且配合使用 auto, decltype ,可以得到更加紧凑的函数对象。