【问题标题】:Is there a way to make member function NOT callable from constructor?有没有办法使成员函数不能从构造函数调用?
【发布时间】:2019-08-29 18:23:07
【问题描述】:

我有使用

的成员函数(方法)
std::enable_shared_from_this::weak_from_this() 

简而言之:weak_from_thisweak_ptr 返回到this。一个警告是它不能从构造函数中使用。 如果有人从继承类的构造函数中使用我的函数,则其中的weak_from_this 将返回过期的weak_ptr。我通过断言检查它没有过期来防止这种情况发生,但它是一个运行时检查。

有没有办法在编译时检查它?

【问题讨论】:

  • 请注意,子类构造函数体和父类构造函数的作用域有所不同:后者在你开始初始化子类的成员(如果有的话)之前就已经完全执行了,更不用说输入了子类构造函数体。
  • 好问题。一种方法是使用纯虚函数weak_from_this 创建一个虚拟类并从中继承你的。这将使它成为一个硬编译错误。
  • @SergeyA 你为什么不把它作为答案发布?这里的所有其他人似乎都认为这是不可能的,所以要么你的评论是错误的和误导性的,要么他们是错误的,你应该展示如何实现它。
  • @Bakuriu 好吧,我没有精力把它打磨成完整的答案。这可能不是一个可行的解决方案。
  • 仅仅基于一个对象的静态类型不(也不能)依赖于它的构造完成的事实。请注意,如果是这种情况,您将无法在 T 的 ctor 中将 this 存储在全局 obj 寄存器中,并在以后使用 obj,w/oa 强制转换,因为存储的 ptr 将具有“ (建设中)T*" 不普通 T*.

标签: c++ constructor c++17 shared-ptr weak-ptr


【解决方案1】:

恐怕答案是“不,不可能在编译时防止这种情况发生。”总是很难证明是否定的,但请考虑一下:如果可以通过这种方式保护函数,那么标准库本身中的 weak_from_thisshared_from_this 可能已经这样做了。

【讨论】:

    【解决方案2】:

    不,没有办法。考虑:

    void call_me(struct widget*);
    
    struct widget : std::enable_shared_from_this<widget> {
        widget() {
            call_me(this);
        }
    
        void display() {
            shared_from_this();
        }
    };
    
    // later:
    
    void call_me(widget* w) {
        w->display(); // crash
    }
    

    问题是你想检查在构造函数中没有调用shared_from_this 是有原因的。想想那个原因。不是不能调用shared_from_this,而是因为它的返回值还没有被赋值的方式。这也不是因为它永远不会被分配。这是因为它会在代码的执行中稍后被分配。操作顺序是程序的运行时属性。您不能在编译时断言操作顺序,这是在运行时完成的。

    【讨论】:

      【解决方案3】:

      不是这样,但是 - 如果性能不是问题,您可以添加一个指示构造完成的标志,并使用它在运行时通过此类调用失败:

      class A {
      
          // ... whatever ...
      public:
          A() { 
              // do construction work
              constructed = true;
          }
      
          foo() {
              if (not constructed)  { 
                  throw std::logic_error("Cannot call foo() during construction"); 
              }
              // the rest of foo
          }
      
      protected:
          bool constructed { false };
      }
      

      您也可以使这些检查仅在调试模式下编译时应用(例如,使用预处理器进行条件编译 - #ifndef NDEBUG),这样在运行时您就不会受到性能损失。不过请注意noexcepts。

      投掷的替代方法是assert()'ing。

      【讨论】:

      • 因为显然没有编译时解决方案不会降低代码的可读性,所以我决定使用assert(!wptr.expired())。我认为这比例外更合适一点,因为没有办法从这种情况中恢复过来。
      • @Korri 请记住,asserts 通常在发布版本中编译出来,所以什么都不会发生。然而,一个例外不是,所以它仍然会在发布版本中终止程序(如果没有被捕获和吞下)。你可以两者都做;首先是assert,然后是throw,或者只是throw
      猜你喜欢
      • 2021-11-11
      • 2011-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-01
      • 1970-01-01
      相关资源
      最近更新 更多