I am trying to work with the lambda expression to wrap some functions to void() ones. The code is as follows:
class VoidFunctionWrapper
{
public:
VoidFunctionWrapper()
{
function_ = []() {};
}
template <typename Function, typename... Args>
VoidFunctionWrapper(Function &&function, Args &&...args)
{
wrap_function(std::forward<Function>(function), std::forward<Args>(args)...);
}
void wrap_function(std::function<void()> function)
{
function_ = std::move(function);
}
template <typename Function, typename... Args>
std::enable_if_t<!std::is_same_v<std::decay_t<Function>, std::function<void()>>, void>
wrap_function(Function &&function, Args &&...args)
{
std::function<void()> function_lambda = [&]() mutable
{ function(std::forward<Args>(args)...); };
wrap_function(function_lambda);
}
std::function<void()> get_wrapped_function()
{
return function_;
}
private:
std::function<void()> function_;
};
The class VoidFunctionWrapper
wraps a function to a std::function<void()>
instance. The key member function is:
template <typename Function, typename... Args>
std::enable_if_t<!std::is_same_v<std::decay_t<Function>, std::function<void()>>, void>
wrap_function(Function &&function, Args &&...args)
{
std::function<void()> function_lambda = [&]() mutable
{ function(std::forward<Args>(args)...); };
wrap_function(function_lambda);
}
To state at the begining, I understand this function is not right because the capture list should use [=]
instead of [&]
. However, when using this "wrong" version of code with [&]
, I am really confused doing this test:
int main()
{
// A test function to set a bool variable to true with a bool pointer
// It changes the value and print the address
std::function<void(bool *)> check = [](bool *ptr)
{
std::cout << ptr << std::endl;
*ptr = true;
};
// Configure some tasks to execute
std::vector<VoidFunctionWrapper> tasks_to_execute;
std::deque<bool> check_list(5, false);
for (size_t i_check = 0; i_check < check_list.size(); i_check++)
{
VoidFunctionWrapper temp_func = VoidFunctionWrapper(check, &(check_list[i_check]));
tasks_to_execute.push_back(std::move(temp_func));
}
for (size_t i_check = 0; i_check < check_list.size(); i_check++)
{
tasks_to_execute[i_check].get_wrapped_function()();
}
for (size_t i_check = 0; i_check < check_list.size(); i_check++)
{
std::cout << check_list[i_check] << " ";
}
std::cout << std::endl;
}
The result is:
0x7f9506008804
0x7f9506008804
0x7f9506008804
0x7f9506008804
0x7f9506008804
0 0 0 0 1
I noticed that to pass a &(check_list[i_check])
may be incorrect when using the capture list [&]
. This r-value &(check_list[i_check])
may exceed the survival period when the final wrapped function is called. I am really confused about the behaviour of my code. I am expecting a "dangling pointer" when trying to call the wrapped function, however, this does not happen.
To make it short, I am wondering how the r-value &(check_list[i_check])
captured in the lambda expression works, making all VoidFunctionWrapper
instances change the same variable's value.
You do have dangling references, and the use of them has undefined behaviour. What you are surprised by is the particular symptoms of the undefined behaviour.
C++ is not a safe language. Experiencing undefined behaviour does not mean the program crashes, it means that you cannot rely on the rules of C++ to predict the program's behaviour. Anything can happen.