【问题标题】:Forbid code to compile if some function is not called如果某些函数没有被调用,则禁止代码编译
【发布时间】:2016-02-23 21:33:09
【问题描述】:

如果没有调用特定函数,C++ 中有没有办法禁止代码编译。

假设我有一些课:

class CExample
{
public:
    void Init();
    void DoWork();

};

如果没有为类对象调用 Init() 函数,有没有办法禁止调用 DoWork()

我想禁止写这样的代码:

CExample e;
e.DoWork();

并允许此版本:

CExample e;
e.Init();
e.DoWork();

我可以通过元编程以某种方式达到这种行为吗?

【问题讨论】:

  • 能不能不把Init的工作放到构造函数里面?
  • 我只是想知道我是否可以做到这一点。这不是我拥有的一些代码。
  • c++ 类不应该有 Init 函数(除非它们是私有的并且对调用者不可用)。 RAII 要求对象要么完全构造,要么抛出异常。这有充分的理由。
  • 如果您没有自己的代码并且无法更改它,请编写一个包装器,在该包装器中在构造函数中调用 Init,而您是您的包装器而不是原始类。
  • 为了编译或链接成功,要求调用函数当然不是语言规范的一部分。人们可能会修改编译器以执行您所描述的操作,但就像人们通常会避免将钉子钉入自己的眼睛一样,人们也会避免这样做。

标签: c++ templates c++11 metaprogramming


【解决方案1】:

您可以只使用构造函数而不是Init

在他的notes about exception safety in the standard library, as appendix to the 3rd edition of The C++ Programming Language 中,Bjarne Stroustrup 讨论了使用init 函数如何与类不变的概念相矛盾。这通常是 Bad Practice™,主要是因为这个原因。

一些旧的 GUI 框架(例如 Microsoft 的 MFC)使用 init 函数来执行特定于派生类的初始化。有other techniques to do that,仅包括passing the required information up the construction chain(通过参数)。

【讨论】:

    【解决方案2】:

    不,那将是糟糕的设计。如果必须调用它才能使对象可用,则应在构造函数中调用它。构造对象后,所有公共方法都应该是可调用的——对象应该完全构造并准备好使用。

    【讨论】:

      【解决方案3】:

      在编译时不知道Init() 是否在DoWork() 之前被调用过。这只能在运行时决定。因此元编程在这里没有用处。

      【讨论】:

      • 可以修改编译器以在编译时或至少在链接时检测到这一点(出于大小优化目的,许多人至少在某些情况下会这样做)。我不知道有什么会拒绝链接,以防万一没有调用。
      • @mah 我的直觉告诉我,这相当于编译时的停机问题。
      • @mah:我不这么认为。调用函数的顺序可能取决于输入数据。这绝对是运行时的事情。
      • @FrankPuffer 啊,是的,我没有把问题解决到它的订购要求上。
      【解决方案4】:

      您应该将您的初始化代码放入构造函数中以强制正确构造该类。但是,如果您真的坚持,并且您的 init 函数确实不是多态的,您可以将 CRTP 与受保护的构造函数一起使用:

      template <typename What>
      class InitMe : public What
      {
      public:
          InitMe() : What() { this->Init(); }
      };
      
      class CExample
      {
      public:
          void Init() {}
          void DoWork() {}
      
      protected:
          CExample() {}
      };
      
      
      int main()
      {
          //CExample e;  // Error: protected constructor.
      
          InitMe<CExample> e;
          e.DoWork();
      }
      

      【讨论】:

        【解决方案5】:

        正如Cheers and hth. -AlfRob K 都提到的那样,您绝对希望在您的类构造函数中执行您的init 工作。必须调用一个单独的函数来确保你的类正确准备好是糟糕的设计。

        不过,话虽如此,您可以检测它是否被调用并采取相应措施:

        void CExample::Init()
        {
            // things
            ...
            init = true;
        }
        
        void CExample::DoWork()
        {
            if (!init)
            {
                Init();
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-03
          • 1970-01-01
          • 2012-05-30
          • 2018-04-08
          • 2021-07-23
          • 2011-08-21
          相关资源
          最近更新 更多