我正在尝试从基本函数转发通用参数包,但这样做很困难,特别是在类型列表中存在非文字非引用类型的情况下
考虑以下示例:
#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
的引用,还对 int
和 float
的引用。
一种可能的解决方案可能是这样的:
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
将其组件传递给正在调用的实际函数。
关于c++ - 如何正确地将参数包转发到 lambda 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70645885/