C++ 防止用户创建类的未命名实例
对于许多RAII“guard”类,将其实例化为匿名变量毫无意义:C++ 防止用户创建类的未命名实例,c++,c++17,raii,C++,C++17,Raii,对于许多RAII“guard”类,将其实例化为匿名变量毫无意义: { std::lock_guard<std::mutex>{some_mutex}; // Does not protect the scope! // The unnamed instance is immediately destroyed. } 发件人: > C++中的匿名变量有“表达式范围”,这意味着它们在表达式创建结束时被破坏。 是否有任何方法可以防止用户在没有名称的情况下实例化
{
std::lock_guard<std::mutex>{some_mutex};
// Does not protect the scope!
// The unnamed instance is immediately destroyed.
}
发件人:
<> > C++中的匿名变量有“表达式范围”,这意味着它们在表达式创建结束时被破坏。
是否有任何方法可以防止用户在没有名称的情况下实例化它们?(“阻止”可能太强-“使其变得非常困难”也是可以接受的) 我可以想到两种可能的解决方法,但它们在类的使用中引入了语法开销:
详细信息命名空间中隐藏该类,并提供一个宏
namespace detail
{
class my_guard { /* ... */ };
};
#define SOME_LIB_MY_GUARD(...) \
detail::my_guard MY_GUARD_UNIQUE_NAME(__LINE__) {__VA_ARGS__}
这是可行的,但有点粗俗
template <typename TArgTuple, typename TF>
decltype(auto) with_guard(TArgTuple&& guardCtorArgs, TF&& f)
{
make_from_tuple<detail::my_guard>(std::forward<TArgTuple>(guardCtorArgs));
f();
}
当guard类的初始化具有“fluent”语法时,此变通方法不起作用:
还有更好的选择吗?我可以使用C++17功能。你可以使用一个可扩展的lint工具,比如Vera++它可以让你对代码进行lint,你可以使用Python或tcl创建新的规则(我更喜欢Python)
一个可能的流程是-在每次提交后,您的CI系统(例如Jenkins)将运行一个作业,执行Vera++并验证这些疏忽,失败时,将向提交者发送邮件。我认为唯一明智的方法是让用户将
guard\u creator::create
的结果传递给某个guard\u activator
,该参数采用左值引用
这样,类的用户就没有办法,只能用名称创建对象(大多数开发人员都会这样做),或者new
it然后取消引用(疯狂选项)
例如,您在评论中说您使用的是非异步链创建者。我可以想象这样一个API:
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
防止类被实例化的规范方法是将其构造函数设置为私有的。要实际获取所需实例之一,可以调用
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
实例没有直接的相似之处,但是如果您能够充分利用C++17的潜力,也许您可以调整这个概念,您可以将使用静态工厂函数的想法扩展为一些有用的内容:受保护的复制省略使静态工厂函数即使对于不可移动的类也是可能的,[[nodiscard]]属性提示编译器在忽略返回值时发出警告
class[[nodiscard]]防护{
公众:
防护(防护和其他)=删除;
~Guard(){/*用_ptr*/}做某事
静态保护创建(void*ptr){返回保护(ptr);}
私人:
守卫(无效*ptr):\u ptr(ptr){}
无效*\u ptr;
};
int main(int,char**){
Guard::create(nullptr);
//auto g=Guard::create(nullptr);
}
我想你问的方向不对。C++开发者应该理解为什么
lock\u-guard
那么明显,我想防止这个错误。@VittorioRomeo这个问题还有其他答案。如果我们能把所有关于这个的讨论集中在一个地方,那就更好了。不管怎么说,看起来很interesting@VittorioRomeo因此,基本上,您使用的是堆栈分配版本的std::async
+future::then
?“实例化为匿名变量毫无意义”,除非结果绑定到引用并延长生存期。这是在C++17之前编写make\u lock\u guard
的唯一方法。一种可能是强制使用make
函数,然后将其标记为[[nodiscard]]
regards@Danh他们修复了一个问题,很久以前,无论有没有/Za
,这个问题都被编译成功。1900表示这是VS2015@Danh您可以使用/we4239进行编译,将该扩展视为错误。对于我的特殊情况,这看起来非常有希望。我会尽快试用它-谢谢,我认为这甚至不应该编译;您正在将右值绑定到左值引用。
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
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()