嵌套 lambda 中的 C++ 完美转发
C++ perfect forwarding in nested lambda
我有一个案例,我正在构造一个特殊的线程对象。该对象必须以与 std::thread 相同的方式接受可调用对象,进行一些验证,然后将其包装在另一个函数中,该函数会做一些额外的事情(确切的理由很复杂,与这个问题无关)。我有一个有效的解决方案,但我不相信它是最佳的,因为我还没有设法使转发工作完美。
我创建了以下示例来调试问题并尝试了解我的错误所在。
该示例编译并运行没有问题。但是 googletest 的地址清理器给我这个错误:
AddressSanitizer: stack-use-after-scope on address 0x7ffcea0a8ff0 at pc 0x00000052a019 bp 0x7fee283febb0 sp 0x7fee283feba8
在示例中,我有一个名为 safe_function_executer
的函数。在这个安全函数中,最外层的 lambda 按值捕获函数和参数。我还有一个名为 bad_function_executer
的函数,我在其中尝试通过引用捕获函数和参数来完美转发。
Googletest 的地址清理程序不会为 safe_function_executer
抛出错误,但会为 bad_function_excecuter
.
抛出错误
我很难理解我在何处访问超出本示例范围的值。有谁知道为什么 Googletest 的地址清理程序会抛出此错误?
#include <atomic>
#include <thread>
#include <array>
#include <functional>
#include <iostream>
#include <chrono>
//!!!WARNING Contrived Example Follows!!!
template<class SomeType, class F, class ...Args>
void a_function_running_function( SomeType& some_arg, F&& f, Args&&... args)
{
f(std::forward<Args>(args)...);
*some_arg = 42;
}
template<class SomeType, class F, class ...Args>
std::thread safe_function_executer( SomeType& some_arg, F&& f, Args&&... args )
{
return std::thread( [=]() mutable {
a_function_running_function( some_arg, [&]() mutable {
f( std::forward<Args>(args)... ); });});
}
template<class SomeType, class F, class ...Args>
std::thread bad_function_executer( SomeType& some_arg, F&& f, Args&&... args )
{
return std::thread( [&,some_arg]() mutable {
a_function_running_function( some_arg, [&]() mutable {
f( std::forward<Args>(args)... ); });});
}
void some_function(int arg1, bool arg2, std::tuple<int,bool>& ret)
{
std::get<0>(ret) = arg1;
std::get<1>(ret) = arg2;
}
int main()
{
auto arg = std::make_shared<std::atomic<int>>(0);
auto ret = std::tuple<int,bool>{0, false};
//works (but not perfectly forwarded?)
auto good_thread = safe_function_executer( arg, &some_function,
45, true, ret );
good_thread.join();
//address sanitizer errors
auto bad_thread = bad_function_executer( arg, &some_function,
45, true, ret );
bad_thread.join();
}
您传递给 bad_function_executer
的所有参数都是临时参数,一旦 bad_function_executer
returns 就会超出主线程的范围。临时对象消失了,但您仍然在另一个线程的 lambda 中使用对它们的引用。
在你的好版本中,你按值捕获 args
,制作它们的本地副本,它在 lambda 的整个生命周期中保持不变。
如果您将它们全部设为左值并以这种方式传递它们,那么它们将保留在范围内直到 join() 调用,这将允许使用 bad_function_executer
.
int arg1 = 45;
bool arg2 = true;
//address sanitizer errors
auto bad_thread = bad_function_executer( arg, &some_function,
arg1, arg2, ret );
但我认为在这种情况下,您最好只按值捕获,就像您对 good
版本所做的那样。
我有一个案例,我正在构造一个特殊的线程对象。该对象必须以与 std::thread 相同的方式接受可调用对象,进行一些验证,然后将其包装在另一个函数中,该函数会做一些额外的事情(确切的理由很复杂,与这个问题无关)。我有一个有效的解决方案,但我不相信它是最佳的,因为我还没有设法使转发工作完美。
我创建了以下示例来调试问题并尝试了解我的错误所在。
该示例编译并运行没有问题。但是 googletest 的地址清理器给我这个错误:
AddressSanitizer: stack-use-after-scope on address 0x7ffcea0a8ff0 at pc 0x00000052a019 bp 0x7fee283febb0 sp 0x7fee283feba8
在示例中,我有一个名为 safe_function_executer
的函数。在这个安全函数中,最外层的 lambda 按值捕获函数和参数。我还有一个名为 bad_function_executer
的函数,我在其中尝试通过引用捕获函数和参数来完美转发。
Googletest 的地址清理程序不会为 safe_function_executer
抛出错误,但会为 bad_function_excecuter
.
我很难理解我在何处访问超出本示例范围的值。有谁知道为什么 Googletest 的地址清理程序会抛出此错误?
#include <atomic>
#include <thread>
#include <array>
#include <functional>
#include <iostream>
#include <chrono>
//!!!WARNING Contrived Example Follows!!!
template<class SomeType, class F, class ...Args>
void a_function_running_function( SomeType& some_arg, F&& f, Args&&... args)
{
f(std::forward<Args>(args)...);
*some_arg = 42;
}
template<class SomeType, class F, class ...Args>
std::thread safe_function_executer( SomeType& some_arg, F&& f, Args&&... args )
{
return std::thread( [=]() mutable {
a_function_running_function( some_arg, [&]() mutable {
f( std::forward<Args>(args)... ); });});
}
template<class SomeType, class F, class ...Args>
std::thread bad_function_executer( SomeType& some_arg, F&& f, Args&&... args )
{
return std::thread( [&,some_arg]() mutable {
a_function_running_function( some_arg, [&]() mutable {
f( std::forward<Args>(args)... ); });});
}
void some_function(int arg1, bool arg2, std::tuple<int,bool>& ret)
{
std::get<0>(ret) = arg1;
std::get<1>(ret) = arg2;
}
int main()
{
auto arg = std::make_shared<std::atomic<int>>(0);
auto ret = std::tuple<int,bool>{0, false};
//works (but not perfectly forwarded?)
auto good_thread = safe_function_executer( arg, &some_function,
45, true, ret );
good_thread.join();
//address sanitizer errors
auto bad_thread = bad_function_executer( arg, &some_function,
45, true, ret );
bad_thread.join();
}
您传递给 bad_function_executer
的所有参数都是临时参数,一旦 bad_function_executer
returns 就会超出主线程的范围。临时对象消失了,但您仍然在另一个线程的 lambda 中使用对它们的引用。
在你的好版本中,你按值捕获 args
,制作它们的本地副本,它在 lambda 的整个生命周期中保持不变。
如果您将它们全部设为左值并以这种方式传递它们,那么它们将保留在范围内直到 join() 调用,这将允许使用 bad_function_executer
.
int arg1 = 45;
bool arg2 = true;
//address sanitizer errors
auto bad_thread = bad_function_executer( arg, &some_function,
arg1, arg2, ret );
但我认为在这种情况下,您最好只按值捕获,就像您对 good
版本所做的那样。