【问题标题】:How to fix class function behaviour based on initialization parameters如何根据初始化参数修复类函数行为
【发布时间】:2021-03-25 18:34:14
【问题描述】:

我正在创建一个 C++ 类,它在初始化期间采用某些参数,并具有一些基于其私有变量的函数,类似于此处的 compute 函数:

class A {
  public:
    A(int x){
      a = x;
    }
    int compute(int y){
      if (a == 0){
        return y*y;
      }
      else if (a == 1){
        return 2*y;
      }
      else{
        return y;
      }
    }
  private:
    int a;
};

// usage

A myA(1); // private variables set only once
myA.compute(10); // this will check value of a 
myA.compute(1); // this will check value of a

鉴于私有变量是在初始化时设置的,不会再被更改,有没有什么有效的方法可以避免运行时与私有变量相关的条件检查?

感谢您提供任何和所有帮助。谢谢

【问题讨论】:

    标签: c++ function class runtime


    【解决方案1】:

    您可以在 int 上模板化函数 compute() 并将模板值用作参数。你可以在https://godbolt.org/z/14Mh4E看到结果

    class A {
    public:
        A(int x) {
            a = x;
        }
        template <int y>
        constexpr int compute() const {
            if (a == 0) {
                return y * y;
            }
            else if (a == 1) {
                return 2 * y;
            }
            else {
                return y;
            }
        }
    private:
        int a;
    };
    
    // usage
    
    A myA(1); // private variables set only once
    myA.compute<10>(); // this will check value of a 
    myA.compute<1>(); // this will check value of a
    

    【讨论】:

    • 这意味着参数 y 在编译时是已知的,这是不需要的,或者是吗?此外,由于成员 a 不是 constexpr,因此计算函数永远不会被评估为 constexpr,因此将始终执行条件检查。
    【解决方案2】:

    如果您使用例如,您可以避免条件检查。一个函数对象作为成员,并根据变量 a 的值设置它。 无论如何,我认为条件检查不会是大的性能问题。但这当然取决于您的应用。

    #include <functional>
    #include <iostream>
    
    class A {
      public:
        A(int x)
        : a { x } 
        {
          if (a == 0){
            compute = [](int y){ return y*y; };
          }
          else if (a == 1){
            compute = [](int y){ return 2*y; };
          }
          else{
            compute = [](int y){ return y; };
          }
    
        }
    
        
        std::function<int(int)> compute;
        
      private:
        int a;
    };
    
    // usage
    
    
    int main()
    {
     
        A myA(1); // private variables set only once
        std::cout << myA.compute(10) << std::endl;
        std::cout << myA.compute(1) << std::endl;
        return 0;
    }
    

    【讨论】:

    • std::function 相当于虚拟呼叫,不确定它是否比打开compute 中的a 值更快。
    【解决方案3】:

    您可以通过使用constexpr 保证在编译时评估条件。请注意,在这种情况下,constexpr compute(...) 必须使用 C++14,因为只有 C++14 之后的 constexpr 函数才支持多个返回语句。

    #include <iostream>
    
    class A {
      public:
        constexpr A(const int x): a(x) { }
        constexpr int compute(const int y) const {
          // Multiple return statements inside a constexpr function
          // requires C++14 or above.
          if (a == 0) {
            return y*y;
          }
          else if (a == 1) {
            return 2*y;
          }
          else {
            return y;
          }
        }
      private:
        int a;
    };
    
    
    int main() {
      constexpr A myA(1);
      constexpr int num = myA.compute(123);
    
      std::cout << num << std::endl;
    
      return EXIT_SUCCESS;
    }
    

    This page 包含对 constexpr 的很好解释以及示例。

    【讨论】:

    • 请注意,您要求myAy(123) 都是常量表达式,并且也是用法(num)。
    【解决方案4】:

    您可以考虑使用模板类而不是私有成员:

    template< int I >
    class A {
      public:
        int compute( int y ) {
          if constexpr ( I == 0 ) {
            return y * y;
          }
          else if constexpr ( I == 1 ) {
            return 2 * y;
          }
          else {
            return y;
          }
        }
    };
    
    // usage
    A<1> myA;
    myA.compute(10); // compile-time check
    myA.compute(1); // compile-time check
    

    【讨论】:

      【解决方案5】:

      如果参数是运行时值,我看不到避免条件或跳转的最佳方法。

      您可以通过虚拟电话交易您的条件:

      struct A
      {
          virtual ~A() = default;
          virtual int compute(int) = 0;
      };
      
      struct A0 { int compute(int y) override { return y * y; } };
      struct A1 { int compute(int y) override { return 2 * y; } };
      struct AN { int compute(int y) override { return y; } };
      
      std::unique_ptr<A> makeA(int a)
      {
          switch (a) {
              case 0: return std::make_unique<A0>();
              case 0: return std::make_unique<A1>();
              default: return std::make_unique<AN>();
          }
      }
      

      (如果类型在编译时已知,编译器可能会去虚拟化调用)

      “等效”

      struct A
      {
          int (*f)(int); // or even std::function<int(int)> f; if you need capture.
      
          A(int a) : f(a == 0 ? +[](int y) { return y * y; }
                     : a == 1 ? +[](int y) { return 2 * y; }
                              : +[](int y) { return y; })
          {}
      
          int compute(int y) { return f(y); }
      };
      

      (擦除类型更难编译器去虚拟化)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多