C++_Constness - RicoJia/notes GitHub Wiki

========================================================================

const

========================================================================

  1. const (run-time constant)

    • const in a function is a promise for not changing the value of a variable.
      void foo(int i); 
      const int j = 1; 
      foo(j);			//error: you're violating the promise to keep foo constant, but use const_cast instead. 
    • const can be located in read-only memory.
  2. An expression (series of operands and operators, that can be evaluated directly) that can be evaluated at compilation time. It can be used as a constant.Essentially, it's equivalent to copying code

    • motivation: it's possible for it to be evaluated during compile time or runtime. A pure runtime value is not allowed here.
  3. So you need to make sure your value needs to be know during compile time, before run time for sure. Otherwise, they are evaluated at runtime. Benefits: you can get more code compiled before run time (ex, int x = 3)!

  4. const vs constexpr:

    • const members can be initialized during compile time or run time, while constexpr is available during compile time.
    • sometimes const may be converted to compile time constant.
  5. const discards qualifiers: const object calling non-const function

    class Bar{
      public: 
        int baz(){}
    }; 
    foo(const Bar&bar ){
      bar.baz();    // needs const qualifier, like int baz()
    }

========================================================================

Constexpr (compile-time constant)

========================================================================

  1. Basics

    • Motivation: computation can be done during compile time!
    • Criterion for constexpr variables:
      1. Must be a literal type (one of the following):
        • void
        • POD
        • reference
        • array of literal type.
        • class that:
          • trivial dtor (dtor that performs no action)
          • ctor:
            • constexpr
            • aggregate (all non-static members are public, no customized ctor, no base class, no virtual function)
      2. Must be assigned during construction
      3. Ctor params: all literal values, constexpr variables, const expr functions, "usable" const values.
        • usable consts const of integral/ enum type. double is not allowed for historical reasons.
          // Basic case: const must be initialized
          const i;    // wrong 
          
          // Case 1: not const at all
          int i = 5; 
          constexpr auto j = i;     
          
          // Case 2: const is not usable for constexpr
          constexpr double k = 3.0;   // fine 
          const double j = 4.5;
          constexpr double i=j;   //Error: j must be of int/enum
          // const is int, but not usable because not initialized from a const
          int i = 5; 
          const j = i; 
          constexpr k = j;    // Error: j wasn't initialized with const
  2. constexpr function is very different from constexpr varibles:

    1. Constexpr function returns and takes in literal types, built-in and user-defined. (can work with const double, different from constexpr variable)

      class Point{
          constexpr Point(double x, double y) noexcept : x(x), y(y) {}      //OK, because args might be known during compilation. 
          constexpr double getX() const noexcept {return x;}        
          // because ctor is constexpr, we may declare a constexpr object. 
          // Then, we might be able to get X as constexpr as well! 
        }
      
      int main(){
          constexpr Point p1 (1.0, 2.0);      //We can have constexpr because of the constexpr ctor
        }
      • Because Point object ** has constexpr ctor, we can use it as an argument, and return type of another constexpr function**
        // p1 doesn't even have to be const, as long as it has constexpr ctor
        constexpr Point returnPoint(Point& p1){
            return p1;
        }
        
        Point p1(1.0, 2.0); 
        auto mid = returnPoint(p1);   // p1 doesn't even have to be const 
    2. C++11 has two more restrictions, while C++14 lifted them:. In C++14, most functions can be constexpr, because constexpr functions can be evaluated during runtime, too.

      • constexpr functions cannot have void as return type
      • constexpr functions are implicitly const functions.
        class Point{
          // Not valid in C++ 11, but valid in C++14. 1. not const member function 2. void as return type
          constexpr void setPoint(double x, double y){x_ = x; y_ = y; }
        }
      • C++ 11 only supports a function returning constexpr has one statement (the return statement, so use ?: and recursion).
        constexpr int pow(int base unsigned exp) noexcept{return (exp==0?1:base*pow(base, exp-1));}
    3. C++ 14 has lifted more restrictions

      • it can be used during run time as well, if at least one variable is not available during compile time
      • class with constexpr constructors or functions with constexpr return type
          class point{
          public:
            constexpr Point (double x_val = 0, double y_val = 0)noexcept: x(x_val), y(y_val){
            }
            
            constexpr double xValue()const noexcept{
              return x;
            }
          private:
            double x,y;
          }
          
          constexpr Point ret_midpoint(const Point& a, const Point& b) noexcept{
            Point mid((a.Xvalue()+b.xvalue())/2.0, (a.Yvalue()+b.Yvalue())/2.0);
            return mid; 
          }
          constexpr auto point = ret_midpoint(a, b); 
  3. Cautions

    • constexpr is part of interface. So taking it off is hard
    • constexpr std::string 不能在compile time compile。 但你可用const char[]
    • Functions not available at compile time
      • math libraries' pow, mod functions are not available during compile time. So don't use them!!
      • std:: cout is also not available during run time!!
    • constexpr variables must be inline, but inline funcs can be evaluated during compile time
  4. Usage

    • Technically, int arr[LEN], LEN has to be constexpr, and not just const
    • Other situations that require constant expression:
      • integral type
      1. an array bounds
        const int cn=2;
        std::array<int, cn> a2; 
      2. enumerator values
      3. case values
      4. bit field sizes (like sizeof)
      5. value template arguments
      6. static member initializers
    • "symbolic constants: " constants that are literals. Typically, #define is used, but please don't do that.
    • static_assert() requires constexpr
      • simple fix: non-type template params, work for integral.
        template<std::size_t N>	// template non-type params is a compiletime thing. 
        constexpr void f(){
          static_assert(N==42);	 
          }
        
⚠️ **GitHub.com Fallback** ⚠️