将 C 函数和参数作为右值引用传递

Posted

技术标签:

【中文标题】将 C 函数和参数作为右值引用传递【英文标题】:Passing C functions and parameters as rvalue references 【发布时间】:2016-07-13 02:44:14 【问题描述】:

我正在尝试改造两段遗留代码。其中一个实现了函数调用的超时。到目前为止,它已被用于 C++ 方法并且运行良好。

现在,需要实现与旧 C 库类似的超时。我正在尝试为此使用相同的代码,但遇到了问题。

这是代码的简化版本,是我面临的问题。

uint32_t myFunc1()

    return 0;


uint32_t myFunc2(uint32_t a)

    return a;


int main()

    uint32_t dummy = 1;
    timedCall(myFunc1); //compiles fine. 
    timedCall(myFunc2, dummy); //compile errors C2672, C2893


template <class F, class... Args>
uint32_t timedCall(F &&f, Args&&... a)

    try
    
        std::packaged_task<uint32_t(Args...)> myTask(std::bind(f, a...));
        auto res = myTask.get_future();
        std::thread(std::move(myTask), a...).detach(); //This is where the issue is.        

        //Do other stuff
    
    catch(...)
    
        //handle exceptions
    

    return 0; //return something

我收到以下错误:

C2672   'std::invoke': no matching overloaded function found
C2893   Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'

谁能告诉我我做错了什么以及如何解决它?我正在使用 Visual Studio 2015。

【问题讨论】:

你能把代码减少到产生错误所需的最大程度吗?您使用的是什么确切版本的 msvc?没有其他错误信息吗?通常它会给出上下文。 在 C++11 或 C++14 模式下使用 Clang 编译良好。 @Yakk,对不起,应该更具体。我正在使用 Visual Studio 2015。 @amol 我确实说“准确”。更新什么? @JohnZwinck,你是对的,它也是用 VS2015 构建的。错误发生在我无意中遗漏的 get_future() 之后的下一行: std::thread(std::move(myTask), a...).detach(); //这就是问题所在。我已经更新了示例代码。 【参考方案1】:

问题在于 std::thread 不接受 packaged_task,std::thread(f, a...) 工作正常。现在我不会尝试在std::thread 水域发货(edit: Andrei R. 提供了一个很好的解释,详细说明了哪里出了问题),而std::async 将使您的任务更容易:

template <class F, class... Args>
uint32_t timedCall(F &&f, Args&&... a)

    try
    
        auto res = std::async
        (
            std::launch::async,
            std::forward<F>(f),
            std::forward<Args>(a)...
        );
        //Do other stuff
    
    catch(...)
    
        //handle exceptions
    

    return 0; //return something

【讨论】:

非常感谢!这帮助很大!【参考方案2】:

当你使用std::bind(f, a...) 时,你会得到可以用obj() 调用的可调用对象。但是std::packaged_task&lt;uint32_t(Args...)&gt; 的构造函数需要使用obj(a...) 调用的对象。不过,有些编译器可能会忽略额外的参数。

您需要删除bind:

std::packaged_task<uint32_t(Args...)> myTask(f);
auto res = myTask.get_future();
std::thread(std::move(myTask), std::forward<Args>(a)...).detach();

或者改变调用语法:

std::packaged_task<uint32_t()> myTask(std::bind(f, std::forward<Args>(a)...));
auto res = myTask.get_future();
std::thread(std::move(myTask)).detach();

或者(更好)遵循阿空加瓜解决方案

【讨论】:

我建议也将 std::forward 添加到第一个解决方案中。 感谢您的解释。它帮助我理解了我刚刚继承的这段代码中发生了什么。

以上是关于将 C 函数和参数作为右值引用传递的主要内容,如果未能解决你的问题,请参考以下文章

函数不将右值引用作为参数? [复制]

右值引用,移动语义,完美转发

C++11 中的左值引用和右值引用的区别

为什么传递右值引用(X &&)是AS IF传递左值引用(X&)?

右值引用成员变量有啥用

将多个右值和左值传递给函数而不创建重载