Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 防止用户创建类的未命名实例_C++_C++17_Raii - Fatal编程技术网

C++ 防止用户创建类的未命名实例

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++中的匿名变量有“表达式范围”,这意味着它们在表达式创建结束时被破坏。 是否有任何方法可以防止用户在没有名称的情况下实例化

对于许多RAII“guard”类,将其实例化为匿名变量毫无意义:

{
    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++开发者应该理解为什么 LoopyGueGue/Cuth>存在,为什么只要它保护的范围存在,为什么它还活着。试图通过API或内部实现来实现这一点是多余的。@Danh:另一个问题的答案是我的“宏”解决方案。如果我更明确地指出,我正在寻找一个基于非宏的解决方案,你会考虑重新打开这个问题吗?@大卫:对于锁紧装置的例子,我同意。不过,我正在使用“fluent”语法进行一些非分配异步链生成,在这种情况下,匿名链是非常自然的。但是您仍然需要一个名称,否则链的存储会很快消失。它不像
    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()