Why doesn't C++11 implicitly convert lambdas to std::function objects?

前端 未结 3 2039
难免孤独
难免孤独 2021-01-01 16:00

I implemented a generic event emitter class which allows code to register callbacks, and emit events with arguments. I used Boost.Any type erasure to store the callbacks so

3条回答
  •  南笙
    南笙 (楼主)
    2021-01-01 16:26

    A lambda is not a std::function, and std::function is not a lambda.

    A lambda is syntactic sugar to create an anonymous class that looks like this:

    struct my_lambda {
    private:
      int captured_int;
      double captured_double;
      char& referenced_char;
    public:
      int operator()( float passed_float ) const {
        // code
      }
    };
    int captured_int = 7;
    double captured_double = 3.14;
    char referenced_char = 'a';
    my_lambda closure {captured_int, captured_double, referenced_char};
    closure( 2.7f );
    

    from this:

    int captured_int = 7;
    double captured_double = 3.14;
    char referenced_char = 'a';
    auto closure = [=,&referenced_char](float passed_float)->int {
      // code
    };
    closure(2.7);
    

    with the type name of the my_lambda actually being some unnameable type.

    A std::function is a completely different thing. It is an object that does implement operator() with a particular signature, and stores a smart value-semantics pointer to an abstract interface that covers copy/move/invoke operations. It has a templated constructor that can take any type that supports copy/move/operator() with a compatible signature, generates a concrete custom class that implement the abstract internal interface, and stores it in the above mentioned internal value-semantics smart pointer.

    It then forwards operations from itself as a value-type to the abstract internal pointer, including perfect forwarding to the invocation method.

    As it happens, you can store a lambda in a std::function, just like you can store a function pointer.

    But there are a whole myriad of different std::function that could store a given lambda -- anything where the types are convertible to and from the arguments works, and in fact works equally well, as far as the std::function is concerned.

    Type deduction in C++ in templates does not work at the level "can you convert into" -- it is pattern matching, pure and simple. As the lambda is a type unrelated to any std::function, no std::function type can be deduced from it.

    If C++ tried to do that in the general case, it would have to invert a Turing-complete process to determine what (if any) set of types could be passed to the template in order to generate a conversion-compatible instance.

    In theory, we could add "operator deduce template arguments from" to the language, where the implementers of a given template can write code that takes some arbitrary type, and they attempt to tease out "from this type, what template parameters should be used for an instance". But C++ does not have this.

提交回复
热议问题