c++ - 如何正确地将参数包转发到 lambda 中?

标签 c++ templates lambda perfect-forwarding

我正在尝试从基本函数转发通用参数包,但这样做很困难,特别是在类型列表中存在非文字非引用类型的情况下

考虑以下示例:

#include <utility>
#include <iostream>
#include <future>
#include <vector>

template < typename... Args >
class BaseTemplate {
 public:
    BaseTemplate() = default;
    virtual ~BaseTemplate() = default;

    virtual std::future< void > call(Args... args) {
        return std::async(std::launch::async, [this, &args... ] {
            // ..
            first(std::forward<Args>(args)...);
            second(std::forward<Args>(args)...);
            third(std::forward<Args>(args)...);
            // ...
        });
    }

 protected:
    virtual void first(Args...) { /* ... */ }
    virtual void second(Args...) = 0;
    virtual void third(Args...) { /* ... */ }
};


class SomeType {
 public:
    explicit SomeType(std::vector< float >* data) : ptr(data) { /* ... */ }
    ~SomeType() = default;
    // ...
    // protected:
    float member = 5.6;
    std::vector< float >* ptr;
};


class Derived1 : public BaseTemplate< int, float, SomeType > {
 public:
    using Base = BaseTemplate< int, float, SomeType >;

    Derived1() : Base() { /* ... */ }
    ~Derived1() = default;

 protected:
    void second(int, float, SomeType obj) override {
        std::cout << "Derived1::" << __func__ << " (" << obj.member << ")" << std::endl;
        printf("%p\n", obj.ptr);
        for (const auto& val : *(obj.ptr)) {
            std::cout << val << std::endl;
        }
    }
};


class Derived2 : public BaseTemplate< int, float, const SomeType& > {
 public:
    using Base = BaseTemplate< int, float, const SomeType& >;

    Derived2() : Base() { /* ... */ }
    ~Derived2() = default;

 protected:
    void second(int, float, const SomeType& obj) override {
        std::cout << "Derived2::" << __func__ << " (" << obj.member << ")" << std::endl;
        printf("%p\n", obj.ptr);
        for (const auto& val : *(obj.ptr)) {
            std::cout << val << std::endl;
        }
    }
};



int main(int argc, char const *argv[]) {
    std::vector< float > data {0, 1, 2, 3};

    SomeType obj(&data);
    Derived1 foo1;
    Derived2 foo2;

    // auto bar1 = foo1.call(1, 5.6, obj);  // Segmentation fault
    auto bar2 = foo2.call(1, 5.6, obj);  // OK

    // ...

    // bar1.wait();
    bar2.wait();

    return 0;
}    

如果SomeType,一切都会按预期进行。通过引用传递,但如果通过值传递,则会出现段错误。正确的使用方法是什么std::forward<>()为了解释这两种情况?

最佳答案

问题不在于 std::forward 调用;程序在到达它们之前表现出未定义的行为。 call 按值获取一些参数,但内部的 lambda 始终通过引用捕获它们。因此,它最终会保存对局部变量的引用,这些变量在 call 返回后立即被销毁 - 但稍后会调用 lambda,可能是在不同的线程上。此时,所有这些引用都是悬空的 - 不仅是对 SomeType 的引用,还对 intfloat 的引用。


一种可能的解决方案可能是这样的:

virtual std::future< void > call(Args... args) {
    std::tuple<BaseTemplate*, Args...> t{this, args...};
    return std::async(std::launch::async, [t] {
        // ..
        std::apply(&BaseTemplate::first, t);
        std::apply(&BaseTemplate::second, t);
        std::apply(&BaseTemplate::third, t);
        // ...
    });
}

我们将参数存储在元组中 - 按值传递的参数的拷贝,对按引用传递的参数的引用。然后 lambda 按值捕获此元组,并使用 std::apply 将其组件传递给正在调用的实际函数。

Demo

关于c++ - 如何正确地将参数包转发到 lambda 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70645885/

相关文章:

javascript - Meteor Blaze.renderWithData(), 如何传递函数

html - 在自己的网站上实现 Bootstrap 导航栏

java - Java 8 Comparator 类型推断非常困惑

c++ - VC2008 整个程序优化失败

c++ - 模板参数推导顺序

python - 这个用于排序数字的 lambda 如何工作?

java - Java 8 中 '()->{}' 的类型是什么?

c++ - 获取错误无法从C++ 17编译器的间接库继承构造函数

c++ - 从 C++ 异常的构造函数中抛出异常

c++ - Qt::Tool 窗口在应用程序变为非事件状态时消失