我有像类一样的范围保护(这是简化的测试用例):
template<void(*close)()>
struct Guard1
{
template<typename O>
Guard1(O open) { open(); }
~Guard1() { close(); }
};
void close() { std::cout << "close g1\n"; }
int main()
{
Guard1<close> g1 = [](){ std::cout << "open g1\n"; };
}
Run Code Online (Sandbox Code Playgroud)
我修改它,使得close表达式也可以作为lambda给出:
class Guard2
{
std::function<void()> close;
public:
template<typename O, typename C>
Guard2(O open, C close) : close(close)
{
open();
}
~Guard2() { close(); }
};
int main()
{
Guard2 g2(
[](){ std::cout << "open g2\n"; },
[](){ std::cout << "close g2\n"; });
}
Run Code Online (Sandbox Code Playgroud)
但是我必须引入一个额外的字段const std::function<void()>& close;来将lambda从构造函数传递给析构函数.
有没有办法避免这个额外的字段,同时仍然保持lambda(和使用时一个很好的语法)?
由于您只想将其用作 ScopeGuard - 那么您可以确保对您的 const 引用或右值引用close()是有效的。正如其他答案一样,您需要一个成员或基类 - 但这并不是很大的区别。但是你可以将它作为 lambda 的右值引用,而不是std::function具有相当大的性能成本:
template <class Close>
class ScopeGuard {
public:
template <typename Open>
ScopeGuard(Open&& open, Close&& close)
: close(std::forward<Close>(close))
{
open();
}
ScopeGuard(ScopeGuard&& other) : close(std::move(other.close))
{}
~ScopeGuard()
{
close();
}
private:
Close&& close;
};
Run Code Online (Sandbox Code Playgroud)
为了使它更容易使用 - 有这个make功能:
template <class Open, class Close>
auto makeScopeGuard(Open&& open, Close&& close)
{
return ScopeGuard<Close>(std::forward<Open>(open),
std::forward<Close>(close));
}
Run Code Online (Sandbox Code Playgroud)
及用法:
#include <iostream>
using namespace std;
int main()
{
int i = 0;
auto scope = makeScopeGuard([&i]{cout << "Open " << i++ << "\n";},
[&i]{cout << "Close " << i++ << "\n";});
cout << "Body\n";
}
Run Code Online (Sandbox Code Playgroud)
输出:
Open 0
Body
Close 1
Run Code Online (Sandbox Code Playgroud)
我验证它适用于 gcc 和 clang、C++14,没有错误/警告。