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

时间:2018-10-17 00:15:05

标签: c++ lambda perfect-forwarding

在某些情况下,我正在构造一个特殊的线程对象。该对象必须采用与std :: thread相同的方式进行可调用,进行一些验证,然后将其包装在另一个函数中,该函数将执行其他一些操作(确切的原理很复杂,并且与该问题无关)。我有一个可行的解决方案,但我不相信这是最佳选择,因为我还没有设法实现完美的转发工作。

我创建了以下示例来调试问题并尝试了解我的错误。

该示例编译并运行没有问题。但是googletest的地址清理器却给我这个错误:

  

AddressSanitizer:地址0x7ffcea0a8ff0在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();
}

1 个答案:

答案 0 :(得分:3)

要传递给bad_function_executer的所有参数都是临时的,一旦bad_function_executer返回,它们就会超出主线程的范围。临时变量已消失,但是您仍然可以在另一个线程的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版本一样。