Kyr*_*iet 7 c++ lambda templates variadic c++20
我正在使用实体组件系统编写一个简单的游戏。我的组件之一是NativeScriptComponent. 它包含我的脚本的实例。我的想法是,我可以NativeScriptComponent随时创建我的Bind任何类实现Scriptable接口。之后我的游戏的 OnUpdate 函数将自动实例化游戏中的所有脚本并调用它们的 OnUpdate 函数。
我的脚本可以有自己的不同构造函数,因此在绑定脚本时我需要将所有参数转发给它们。
考虑以下代码:
#include <iostream>
#include <memory>
#include <functional>
using namespace std;
struct Scriptable
{
virtual void OnUpdate() {};
};
struct MyScript : Scriptable
{
MyScript(int n) : value(n) {}
void OnUpdate() override {cout << value;}
int value;
};
struct NativeScriptComponent
{
unique_ptr<Scriptable> Instance;
function<unique_ptr<Scriptable>()> Instantiate;
template<typename T, typename ... Args>
void Bind(Args&&... args)
{
// (A)
Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...); };
// (B) since C++20
Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...); };
}
};
int main()
{
NativeScriptComponent nsc;
nsc.Bind<MyScript>(5);
// [..] Later in my game's OnUpdate function:
if (!nsc.Instance)
nsc.Instance = nsc.Instantiate();
nsc.Instance->OnUpdate(); // prints: 5
return 0;
}
Run Code Online (Sandbox Code Playgroud)
1)选项A和B有什么区别
Instantiate = [&args...]() { return make_unique<T>(forward<Args>(args)...); };
Run Code Online (Sandbox Code Playgroud)
与
Instantiate = [...args = forward<Args>(args)]() { return make_unique<T>(args...); };
Run Code Online (Sandbox Code Playgroud)
为什么我不能在选项 B 中使用forward<Args>(args)inside ?make_unique
A 和 B 都完美转发吗?
为了增加完整性,您可以执行更多操作。正如另一个答案中指出的:
[&args...]() { return make_unique<T>(forward<Args>(args)...); };
Run Code Online (Sandbox Code Playgroud)
这通过引用捕获所有参数,这可能导致悬空。但这些引用已在正文中正确转发。
[...args=forward<Args>(args)]() { return make_unique<T>(args...); };
Run Code Online (Sandbox Code Playgroud)
这会将所有参数转发到 lambda 中,args内部是一组值,而不是引用。这会将它们作为左值全部传递到make_unique.
[...args=forward<Args>(args)]() mutable { return make_unique<T>(move(args)...); };
Run Code Online (Sandbox Code Playgroud)
如上所述,考虑到这args是一组值,并且我们正在创建一个unique_ptr值,我们可能应该将std::move它们放入make_unique. 请注意,lambdaargs在这里有其自己的内部,因此移动它们不会影响传递到该函数中的任何左值。
[args=tuple<Args...>(forward<Args>(args)...)]() mutable {
return std::apply([](Args&&... args){
return std::make_unique<T>(forward<Args>(args)...);
}, std::move(args));
};
Run Code Online (Sandbox Code Playgroud)
最有趣的选择。在我们通过引用捕获所有参数或转发所有参数(复制左值并移动右值)之前。但还有第三种选择:我们可以通过引用捕获左值并移动右值。我们可以通过显式捕获一个元组并转发到其中来做到这一点(请注意,对于左值,模板参数是,T&对于右值,它是T- 所以我们通过值获取右值)。
一旦我们有了元组,我们就apply在内部使用它 - 这会给我们Args&&...返回(注意&&和 这不是一个通用的 lambda,也不需要是)。
这是对以前版本的改进,因为我们不需要复制左值 - 或者可能更糟糕,因为现在我们有更多的机会进行悬空。
四种解决方案的比较:
| 选项 | 可以悬挂吗? | 左值 | 右值 |
|---|---|---|---|
&args... |
左值和右值 | 1份 | 1 步 |
...args=FWD(args)和args... |
不 | 2份 | 1 次移动,1 次复制 |
...args=FWD(args)和move(args)... |
不 | 1份副本,1次移动 | 2 步 |
args=tuple<Args...> |
左值 | 1份 | 2 步 |
| 归档时间: |
|
| 查看次数: |
172 次 |
| 最近记录: |