对于许多 RAII“后卫”类,被实例化为匿名变量根本没有意义:
{
std::lock_guard<std::mutex>{some_mutex};
// Does not protect the scope!
// The unnamed instance is immediately destroyed.
}
{
scope_guard{[]{ cleanup(); }};
// `cleanup()` is executed immediately!
// The unnamed instance is immediately destroyed.
}
来自this article:
C ++中的匿名变量具有“表达式范围”,这意味着它们在创建它们的表达式的末尾被销毁。
有没有办法阻止用户在没有名字的情况下实例化它们? (“防止”可能太强 - “使其非常困难”也是可以接受的)。
我可以想到两种可能的解决方法,但它们会在使用类时引入语法开销:
隐藏detail
命名空间中的类并提供宏。
namespace detail
{
class my_guard { /* ... */ };
};
#define SOME_LIB_MY_GUARD(...) \
detail::my_guard MY_GUARD_UNIQUE_NAME(__LINE__) {__VA_ARGS__}
这有效,但 hackish 。
仅允许用户通过高阶函数使用警卫。
template <typename TArgTuple, typename TF>
decltype(auto) with_guard(TArgTuple&& guardCtorArgs, TF&& f)
{
make_from_tuple<detail::my_guard>(std::forward<TArgTuple>(guardCtorArgs));
f();
}
用法:
with_guard(std::forward_as_tuple(some_mutex), [&]
{
// ...
});
当防护类的初始化具有“流畅”语法时,此解决方法不起作用:
{
auto _ = guard_creator()
.some_setting(1)
.some_setting(2)
.create();
}
还有更好的选择吗?我可以访问C ++ 17的功能。
答案 0 :(得分:5)
我想到的唯一明智的方法是让用户将guard_creator::create
的结果传递给某个guard_activator
,其中以左值参考作为参数。
这样,类的用户无法创建具有名称的对象(大多数开发人员将使用的理智选项),或者new
然后取消引用(疯狂选项)
auto token = monad_creator().then([]{...}).then([]{...}).then([]{...}).create();
launch_async_monad(token); //gets token as Token&, the user has no way BUT create this object with a name
答案 1 :(得分:3)
如果能充分发挥C ++ 17的潜力,你可以将使用静态工厂函数的想法扩展到有用的东西:保证复制省略使静态工厂功能成为可能,即使是非可移动的类,[ [nodiscard]]属性会提示编译器在忽略返回值时发出警告。
class [[nodiscard]] Guard {
public:
Guard(Guard& other) = delete;
~Guard() { /* do sth. with _ptr */ }
static Guard create(void* ptr) { return Guard(ptr); }
private:
Guard(void* ptr) : _ptr(ptr) {}
void* _ptr;
};
int main(int, char**) {
Guard::create(nullptr);
//auto g = Guard::create(nullptr);
}
答案 2 :(得分:0)
你可以使用一个可扩展的lint工具,例如Vera ++ https://bitbucket.org/verateam/vera/wiki/Home,它可以让你搞砸你的代码,你可以使用Python或tcl创建新规则(我更喜欢Python)
可能的流程是 - 在每次提交后,您的CI系统(例如Jenkins)将运行一个执行Vera ++的作业并验证此类疏忽,一旦发生故障,将向提交者发送邮件。
答案 3 :(得分:0)
阻止类实例化的规范方法是创建构造函数private
。要实际获取所需实例之一,请调用static
方法,该方法返回对构造对象的引用。
class Me {
public:
static Me &MakeMe() { return Me(); }
private:
Me();
}; // Me
这当然没有用 - 但它可能会让程序员暂停!
int main() {
Me(); // Invalid
Me m; // Invalid
Me::MakeMe(); // Valid - but who'd write that?
Me m = Me::MakeMe();
} // main()
我知道不是直接模仿你所描述的Guard
个实例 - 但也许你可以调整这个概念吗?