如何正确声明仅使用lambda的方法?

时间:2020-10-11 15:04:10

标签: c++ lambda

在下面的示例中,我想要一个traverse方法来接收回调。一旦我没有捕获任何[],此示例就可以完美运行,因为可以将lambda简化为函数指针。但是,在这种情况下,我想访问sum

struct Collection {
    int array[10];

    void traverse(void (*cb)(int &n)) {
        for(int &i : array)
            cb(i);
    }

    int sum() {
        int sum = 0;
        traverse([&](int &i) {
            sum += i;
        });
    }
}

解决此问题的正确方法是什么(不使用任何模板)?一种解决方案是按如下方式使用typename模板。但是在这种情况下,您无法了解每次迭代(int)中遍历的含义:

template <typename F>
void traverse(F cb) {
    for(int &i : array)
        cb(i);
}

3 个答案:

答案 0 :(得分:2)

Lambda类型未指定;无法命名它们。

因此,您有两种选择:

  • traverse用作模板(或使用auto,实际上是同一件事)

    幸运的是,这是一件完全正常且平常的事情。

  • traversestd::function<void(int)>。这会产生一些开销,但至少意味着该功能不必是模板。

但是在这种情况下,您无法了解每次迭代的遍历结果(整数)

我们不倾向于考虑这个问题。我确实知道,在函数的类型中提供此名称更为令人满意和明确,但是通常注释就足够了,因为如果回调提供int,您将获得一个编译错误。

答案 1 :(得分:2)

只有不可捕获的lambda可以与函数指针一起使用。由于每个lambda定义都有自己的类型,因此您必须在所有接受捕获的lambda的地方都使用模板参数。

但是在这种情况下,您无法了解每次迭代的遍历结果(整数)。

使用SFINAE可以轻松地检查此问题,或者使用C ++ 20中的概念甚至可以更简单地检查此问题。为了使它更简单,您甚至不需要定义一个概念并在以后使用它,您可以直接使用即席要求(这会导致requires关键字的双重使用:< / p>

struct Collection {
    int array[10];


    template <typename F>
        // check if F is "something" which can be called with an `int&` and returns void.
        requires requires ( F f, int& i) { {f(i)} -> std::same_as<void>; }
        void traverse(F cb) 
        {   
            for(int &i : array)
                cb(i);
        }

     // alternatively you can use `std::invocable` from <concepts>

    // check if F is "something" which can be called with an `int&`, no return type check 
    template <std::invocable<int&> F>
        void traverse2(F cb) 
        {   
            for(int &i : array)
                cb(i);
        }   

    int sum() {
        int sum = 0;
        traverse([&](int &i) {
            sum += i;
        });

        return sum;
    }
};

答案 2 :(得分:0)

在您的情况下,您可以通过几种方法在C ++中声明回调:

函数指针

void traverse(void (*cb)(int &n)) {
    for(int &i : array)
        cb(i);
}

此解决方案仅支持可以衰减为函数指针的类型。如您所述,具有捕获功能的Lambda不会成功。

类型名称模板

template <typename F>
void traverse(F cb) {
    for(int &i : array)
        cb(i);
}

它确实接受任何东西,但是正如您所注意到的。该代码很难阅读。

标准函数(C ++ 11)

void traverse(std::function<const void(int &num)>cb) {
    for(int &i : array)
        cb(i);
}

这是功能最全的解决方案,开销成本略高。

别忘了包含<functional>