搜索避免在类模板中创建字段的技巧
Searching for a trick to avoid creating a field in a class template
我有一个类似类的作用域保护(这是简化的测试用例):
template<void(*close)()>
struct Guard1
{
template<typename O>
Guard1(O open) { open(); }
~Guard1() { close(); }
};
void close() { std::cout << "close g1n"; }
int main()
{
Guard1<close> g1 = [](){ std::cout << "open g1n"; };
}
我修改了它,以便关闭表达式也可以作为 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 g2n"; },
[](){ std::cout << "close g2n"; });
}
但是,我必须引入一个额外的字段const std::function<void()>& close;
将lambda从构造函数传递到析构函数。
有没有办法避免这个额外的字段,同时仍然保留lambda(以及使用时也很好的语法)?
由于您只想将其用作 ScopeGuard - 因此您可以确定对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;
};
为了使其更易于使用 - 具有以下make
功能:
template <class Open, class Close>
auto makeScopeGuard(Open&& open, Close&& close)
{
return ScopeGuard<Close>(std::forward<Open>(open),
std::forward<Close>(close));
}
和用法:
#include <iostream>
using namespace std;
int main()
{
int i = 0;
auto scope = makeScopeGuard([&i]{cout << "Open " << i++ << "n";},
[&i]{cout << "Close " << i++ << "n";});
cout << "Bodyn";
}
输出:
Open 0
Body
Close 1
我验证了它适用于 gcc 和 clang,C++14 没有错误/警告。
这通常是不可能的。与函数指针不同,lambda 可以捕获并因此包含运行时状态。诚然,您的 lambda 没有,因此可以转换为函数指针,但这并不能使其成为模板参数。
如果你能接受一点作弊:将lambda塞进基类而不是字段中:
#include <iostream>
template<typename Base>
struct Guard1 : public Base
{
template<typename O>
Guard1(O open, Base base ) : Base(base) { open(); }
Guard1(Guard1 const& rhs) : Base(static_cast<Base const&>(rhs)) { }
~Guard1() { (*this)(); }
};
template<typename O, typename C>
Guard1<C> makeGuard(O o, C c) { return Guard1<C>(o,c); }
int main()
{
auto g1 = makeGuard([](){ std::cout << "open g1n"; },
[](){ std::cout << "close g1n"; } );
}
有没有办法避免这个额外的字段,同时仍然保留lambda(以及使用时也很好的语法)?
是的:如果你观察到,将开放函数传递给你的范围守卫没有任何好处(因此单一责任原则规定你不应该在那里拥有它)。
还应将该函数作为运行时参数传递,而不是模板参数。这将允许在客户端代码中使用更自然的语法。
应使类型独立于模板类型。这也将使客户端代码中的语法更加自然。
应确保析构函数不会引发。
class Guard final
{
public:
Guard1(std::function<void()> at_scope_exit)
: f_(std::move(at_scope_exit))
{
}
~Guard1() noexcept { try{ f_(); } catch(...) {} }
private:
std::function<void()> f_;
};
然后,客户端代码应如下所示:
int x()
{
operation.begin_transaction();
Guard commit{ [&operation](){ operation.commit_transaction(); } };
// do things & stuff here
}
也许你可以使用这个问题的答案。希望我没有错,但如果可以使用构造函数的指针,您可以将其传递给type_traits
(查看该问题中的第一个答案),并获得第二个参数,这将是关闭函数,然后您可以别名它。
由于无法获取构造函数的指针,也许您可以使用其他成员函数来初始化您的对象?
- 创建 10x10 的按钮字段
- 如何创建一个函数来提取向量内部字符串中的字段?
- 用于基于成员字段或函数创建比较器的快捷方式
- C++创建一个链表,每个节点有超过 2 个字段
- C MPI创建并发送具有字段char [16]和整数的结构数组
- 安全的方法来为char []字段创建Getter
- 如何为我的 Vulkan 类创建此通用数据结构字段
- C++ 如何创建位图字段
- 在抽象类中,可以使用抽象类A的类型创建一个静态字段
- 如何创建具有4个字段和每个字段9个字符长度的矢量对象
- 当您有结构名称和字段值的列表时,是否可以以编程方式创建结构的对象并填充字段
- 如何在容器项的字段上创建迭代器?
- 剪辑在 c++ 中创建多字段
- 在 Qt 创建器中禁用 -wmissing 字段初始值设定项警告
- C++ 如何在结构向量的一个字段上创建迭代器
- 如何创建一个包含用户在程序中输入的 3 个字段的结构
- ADO 创建参数在尝试从 C++ 写入 Oracle 的 RAW(16) 字段时失败并出现0x800A0D5D
- 原始数据包创建导致 IP 字段顺序不正确
- 在Visual c++ 2010中不能使用default_random_engine字段创建类
- C++-有没有一种方法可以为字段创建别名