Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing lambda declared using auto-keyword by non-const reference as argument to std::function parameter type

Tags:

c++

c++11

lambda

Consider the following code:

#include <iostream>
#include <functional>

// passing function object by value
void foo(std::function<int(int, int)> f) {
    std::cout << f(3, 7) << std::endl;
}

// passing const function object by reference
void bar(const std::function<int(int, int)> & f) {
    std::cout << f(3, 7) << std::endl;
}

// passing non-const function object by reference
void fizz(std::function<int(int, int)> & f) {
    std::cout << f(3, 7) << std::endl;
}

int main() {

    std::function<int(int, int)> g1 = [] (int x, int y) { return x * y; };
    auto g2 = [] (int x, int y) { return x * y; };

    foo(g1);   // OK
    foo(g2);   // OK
    bar(g1);   // OK
    bar(g2);   // OK
    fizz(g1);  // OK

    // compile error:
    // error: invalid initialization of reference of type
    // 'std::function<int(int, int)>&' from expression of type
    // 'main()::<lambda(int, int)>'
    // error: in passing argument 1 of 'void fizz(std::function<int(int, int)>&)'
    fizz(g2);

}

Would someone explain to me:

(a) Why fizz(g2) generates a compile error, and the other constructs do not? It seems that I can pass a lambda by reference if I explicitly type out its type in its declaration, but not if I use the auto keyword, OR if I declare the function parameter type as const.

(b) What does const in the function parameter type mean here?

(c) In what circumstances would I prefer to pass by value rather than reference (UPDATE: now a separate question: C++11: pass (lambda or other) function object by reference or value?)?

Thanks for any insight.

like image 545
Jeet Avatar asked Apr 13 '13 05:04

Jeet


1 Answers

In your fizz(g1) case, g1 is already a std::function. Therefore, there is no need for an implicit conversion. So fizz's parameter is a non-const reference to g1.

In fizz(g2), g2 is not a std::function. Therefore, the compiler must perform an implicit conversion. And the implicit conversion returns a temporary.

Temporary objects cannot be bound to non-const references; they can only bind to const references or values. Hence the compiler error.

What does const in the function parameter type mean here?

You can only call const members of the object, you can not implicitly convert it to non-const, and you cannot perform non-const operations on its members.

You know, just like any other use of const.

Generally speaking, when a function takes a parameter by non-const reference, it is saying something very special about what it intends to do with that parameter. Namely, that you are handing it an object, and it is going to do non-const things with it (ie: modifying that object). That's why temporaries don't bind to non-const references; it's likely to be a mistake by the user, since the temporary may not update the original object you passed.

Unless fizz is actually going to modify the object, you should pass it either by value or by const&.

like image 78
Nicol Bolas Avatar answered Oct 26 '22 00:10

Nicol Bolas