简体   繁体   English

嵌套lambda中的C ++完美转发

[英]C++ perfect forwarding in nested lambda

I have a case where I am constructing a special thread object. 我有一个案例,我正在构建一个特殊的线程对象。 This object must take a callable in the same way the std::thread does, do some validation, and then wrap it in another function which does some additional things (exact rationale is complicated and not relevant to this question). 这个对象必须采用与std :: thread相同的方式进行调用,进行一些验证,然后将其包装在另一个执行一些其他操作的函数中(确切的理由是复杂的,与此问题无关)。 I have a working solution but I am not convinced that it is optimal as I have not managed to get perfect forwarding working. 我有一个有效的解决方案,但我不相信它是最佳的,因为我没有设法完美的转发工作。

I created the following example to debug the issue and try to understand what my error is. 我创建了以下示例来调试问题并尝试了解我的错误。

The example compiles and runs without issue. 该示例编译并运行没有问题。 However googletest's address sanitizer gives me this error: 但googletest的地址清理程序给了我这个错误:

AddressSanitizer: stack-use-after-scope on address 0x7ffcea0a8ff0 at pc 0x00000052a019 bp 0x7fee283febb0 sp 0x7fee283feba8 AddressSanitizer:在地址0x7ffcea0a8ff0上的堆栈使用后,在pc 0x00000052a019 bp 0x7fee283febb0 sp 0x7fee283feba8

In the example I have a function called safe_function_executer . 在示例中,我有一个名为safe_function_executer的函数。 In this safe function the outermost lambda captures the function and arguments by value. 在这个安全函数中,最外层的lambda按值捕获函数和参数。 I also have a function called bad_function_executer in which I attempt perfect forwarding by capturing the function and arguments by reference. 我还有一个名为bad_function_executer的函数,我通过引用捕获函数和参数来尝试完美转发。

Googletest's address sanitizer does not throw an error for the safe_function_executer but it does for the bad_function_excecuter . Googletest的地址清理程序不会为safe_function_executer抛出错误,但会为bad_function_excecuter抛出错误。

I am having difficulty understanding where I am accessing a value that has gone out of scope in this example. 我很难理解我在这个示例中访问超出范围的值的位置。 Does anyone know why Googletest's address sanitizer would be throwing this error? 有谁知道为什么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();
}

All the parameters you are passing to bad_function_executer are temporaries and go out of scope in your main thread as soon as bad_function_executer returns. 传递给bad_function_executer所有参数都是临时的,一旦bad_function_executer返回,就会bad_function_executer主线程的范围。 The temporaries are gone but you still use a reference to them in your lambda in the other thread. 临时工已经消失,但你仍然在另一个线程中使用lambda中的引用。

In your good version, you capture the args by value, making a local copy of them, which stays around through the lifetime of the lambda. 在你的好版本中,你可以按值捕获args ,制作它们的本地副本,它们在lambda的生命周期内保持不变。

If you made them all lvalues and passed them in that way, then they would remain in scope until the join() call, which would allow things to work with bad_function_executer . 如果你将它们全部变为lvalues并以这种方式传递它们,那么它们将保持在范围内,直到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 );

but I think you're better off just capturing by value in this case, as you do with your good version. 但是我认为你最好只是在这种情况下通过价值捕获,就像你使用good版本一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM