是否有可能在lambda中捕获可变数量的参数?

时间:2016-02-18 06:31:34

标签: c++ gcc lambda

考虑以下一组示例。

  1. 函数takeOnlyVoidFunction采用零参数的函数并简单地执行它。
  2. 函数takeVariableArguments使用可变数量的参数并使用参数执行函数。
  3. 函数captureVariableArgs尝试将第二个函数转换为第一个函数可接受的lambda形式,但它不会编译。
  4. 如何使函数captureVariableArgs编译并展示将具有可变数量参数的函数转换为不带参数的闭包的正确行为?

    #include <stdio.h>
    #include <functional>
    
    void takeOnlyVoidFunction(std::function<void()> task) {
        task();
    }
    
    template<typename _Callable, typename... _Args>
        void takeVariableArguments(_Callable&& __f, _Args&&... __args) {
         __f(__args...);
    }
    
    // How can I make this function compile?
    template<typename _Callable, typename... _Args>
        void captureVariableArgs(_Callable&& __f, _Args&&... __args) {
        takeOnlyVoidFunction([=]() { __f(__args...);});
    }
    
    void normalFunction(int a, int b) {
        printf("I am a normal function which takes params (%d,%d)\n", a, b);
    }
    
    int main() {
        int a = 7;
        int b = 8;
        takeVariableArguments(normalFunction, a, b);
        takeOnlyVoidFunction([=](){ normalFunction(a,b);});
        captureVariableArgs(normalFunction, a, b);
    }
    

    我正在运行gcc 4.9.2。这是我看到的编译器错误。

    g++ -std=c++11    Test.cc   -o Test
    Test.cc: In instantiation of ‘captureVariableArgs(_Callable&&, _Args&& ...)::<lambda()> [with _Callable = void (&)(int, int); _Args = {int&, int&}]’:
    Test.cc:16:38:   required from ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’
    Test.cc:16:50:   required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
    Test.cc:28:45:   required from here
    Test.cc:16:34: error: variable ‘__f’ has function type
         takeOnlyVoidFunction([=]() { __f(__args...);});
                                      ^
    Test.cc:16:34: error: variable ‘__f’ has function type
    Test.cc: In instantiation of ‘struct captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’:
    Test.cc:16:50:   required from ‘void captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]’
    Test.cc:28:45:   required from here
    Test.cc:16:34: error: field ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>::<__f capture>’ invalidly declared function type
    In file included from Test.cc:2:0:
    /usr/include/c++/4.9/functional:2418:7: error: ‘std::function<_Res(_ArgTypes ...)>::function(_Functor) [with _Functor = captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>; <template-parameter-2-2> = void; _Res = void; _ArgTypes = {}]’, declared using local type ‘captureVariableArgs(_Callable&&, _Args&& ...) [with _Callable = void (&)(int, int); _Args = {int&, int&}]::<lambda()>’, is used but never defined [-fpermissive]
           function<_Res(_ArgTypes...)>::
           ^
    

    更新:演示此问题的更简单示例。

    #include <stdio.h>
    
    // How can I make this function compile?
    template<typename _Callable>
    void captureVariableArgs(_Callable&& __f) {
        takeOnlyVoidFunction( [=]{ __f(); } );
    }
    
    void normalFunction() {
        printf("I am a normal function\n");
    }
    
    int main(){
        captureVariableArgs(normalFunction);
    }
    

2 个答案:

答案 0 :(得分:2)

帖子中的代码可以使用最新的clang&amp; MSVC编译器进行编译,但所有gcc都拒绝编译它。所以它似乎是gcc中的一个bug。尽管如此,我找到了让gcc高兴的方法:不要使用&#34;通用参考&#34;在可调用的参数上,如下所示:

template<typename _Callable, typename... _Args>
int captureVariableArgs(_Callable _f, _Args&&... _args) {
    return takeOnlyVoidFunction([=]() { _f(_args...);});
}

我无法解释为什么gcc不接受你的版本。我不熟悉gcc风格的错误报告,无法从错误消息中提取真正的原因。但我认为解决方法是可以的,因为我没有看到&#34;通用参考&#34;在这种情况下。事实上,我也不知道你为什么在args上使用它。

答案 1 :(得分:2)

作为GCC的另一个潜在解决方法,您可以使用std::bind而不是使用lambda:

template <typename F, typename... Args>
auto captureVariable(F&& f, Args&&... args)
{
    return std::bind(std::forward<F>(f), std::forward<Args>(args)...);
}

这适用于GCC 4.9.3。