Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning std::function<int(int)> to std::function<const int&(const int& x)>

Tags:

c++

c++11

The following code compiles but yields undefined output in VC++ 2015 (release) and a runtime error with other compilers.

#include <functional>
#include <iostream>
int main()
{
    std::function<int(int)> f = [](int x) { return x; };
    std::function<const int&(const int& x)> g = f;
    std::cout << g( 42 ) << std::endl;
}

Why is the assignment g = f; allowed?

like image 745
Tobias Hermann Avatar asked Apr 01 '16 14:04

Tobias Hermann


People also ask

Does std :: function allocate?

On g++, a non-trivial copy constructor on a function object, or data exceeding 16 bytes, is enough to cause it to allocate. But if your function object has no data and uses the builtin copy constructor, then std::function won't allocate.

What does std :: function do in C++?

Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions (via pointers thereto), lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

What is the type of std :: function?

std::function is a type erasure object. That means it erases the details of how some operations happen, and provides a uniform run time interface to them. For std::function , the primary1 operations are copy/move, destruction, and 'invocation' with operator() -- the 'function like call operator'.

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.


2 Answers

Consider the equivalent code rewritten to avoid lambdas or std::function:

int f(int x) { return x; } 
int const& g(int const& x) { return f(x); } 

This is perfectly well-formed code, that nevertheless returns a dangling reference to a temporary and thus will end up causing undefined behavior. The original code is vaid for the same reason: you can implicitly convert an object to a reference of the same type. Unfortunate, in this case.

like image 144
Barry Avatar answered Oct 10 '22 03:10

Barry


An rvalue can be bound to a const&. A const& can be converted to an rvalue.

Examine this:

int f(int x){return x;}
int const& g(int const& x){ return f(x); }

Similarly, the call to g is legal, there are no errors, yet reading the result of g(42) is UB -- the reference dangles.

A good compiler will see the reference bound to temporary being returned and warn.

function simply checks if the types can be converted between; it does no lifetime analysis. Possibly it should, as we can detect this error statically.

like image 32
Yakk - Adam Nevraumont Avatar answered Oct 10 '22 04:10

Yakk - Adam Nevraumont