我的班级的成员类型为optional<A>
。我正在尝试实现函数emplaceWhenReady
,该函数获取A的构造函数的参数列表,但不平凡的部分是A
仅在特定事件后才能初始化。在事件发生前调用emplaceWhenReady
时,我需要以某种方式捕获初始化值。
对于单个构造函数参数,代码可以编写为:
struct B {
bool _ready;
std::optional<A> _a;
std::function<void()> _fInit;
template<typename ARG>
void emplaceWhenReady1(ARG&& arg) {
if (_ready) {
_a.emplace(std::forward<ARG>(arg));
} else {
_fInit = [this, argCopy = std::forward<ARG>(arg)]() {
_a.emplace(std::move(argCopy));
};
}
};
现在,当类变为_fInit()
时,可以调用和_ready
。但是我无法为多个参数编写类似的代码:
// Fails to compile
template<typename... ARGS>
void emplaceWhenReady(ARGS&&... args) {
if (_ready) {
_a.emplace(std::forward<ARGS>(args)...);
} else {
_fInit = [this, argsCopy = std::forward<ARGS>(args)...]() {
_a.emplace(std::move(argsCopy)...);
};
}
}
Godbolt:https://godbolt.org/z/Fi3o1S
error: expected ',' or ']' in lambda capture list
_fInit = [this, argsCopy = std::forward<ARGS>(args)...]() {
^
感谢您的帮助!
答案 0 :(得分:1)
如果我们查看提案p0780: Allow pack expansion in lambda init-capture,它将涵盖此问题和可能的解决方案:
随着引入广义lambda捕获[1],lambda 捕获几乎可以任意复杂,几乎可以解决所有问题 问题。但是,功能上仍然有一个尴尬的漏洞 关于参数包的lambda捕获:您只能 通过复制,引用或... std :: tuple捕获包?
考虑一个简单的例子,尝试包装一个函数及其 参数放入可调用对象中,以供以后访问。如果我们复制所有内容, 该实现方式易于编写和阅读:
template<class F, class... Args> auto delay_invoke(F f, Args... args) { // the capture here can also be just [=] return [f, args...]() -> decltype(auto) { return std::invoke(f, args...); }; }
但是,如果我们尝试提高实施效率,并尝试 将所有参数移到lambda中?看来你应该 能够使用初始化捕获并编写:
template<class F, class... Args> auto delay_invoke(F f, Args... args) { return [f=std::move(f), ...args=std::move(args)]() -> decltype(auto) { return std::invoke(f, args...); }; }
但是这与以下措辞非常明确 [expr.prim.lambda.capture] / 17,重点是我的:
简单捕获后跟省略号是包扩展。初始捕获后跟省略号是不正确的。
它讨论了包括使用元组在内的各种解决方案:
由于此限制,我们唯一的选择是将所有 args ...到std :: tuple中。但是一旦这样做,我们就无法访问 到参数作为参数包,所以我们需要将它们拉回 使用std :: apply():
template<class F, class... Args> auto delay_invoke(F f, Args... args) { return [f=std::move(f), tup=std::make_tuple(std::move(args)...)]() -> decltype(auto) { return std::apply(f, tup); }; }
如果我们想对所捕获的内容进行处理,那会变得更糟 参数包是调用命名函数而不是捕获的 宾语。那时,所有的理解力都消失了。 窗口:
该提案为merged in the draft standard in March,因此我们应该在C ++ 2a中进行此更改。
答案 1 :(得分:1)
在一些帮助下(请参见上面的答案和评论),我能够找到的解决方案是:
template<typename... ARGS>
void emplaceWhenReady(ARGS&&... args) {
if (_ready) {
_a.emplace(std::forward<ARGS>(args)...);
} else {
_fInit = [this, argsCopy = std::make_tuple(std::forward<ARGS>(args)...)]() {
auto doEmplace = [&](auto&... params) {
_a.emplace(std::move(params)...);
};
std::apply(doEmplace, argsCopy);
};
}
}