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.
6
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.