Lesson 7 Constant Expressions

Keywords: Java Programming

1. Differences between const and constexpr

(1) When modifying variables, const is "run-time constant", that is, run-time data is read-only.constexpr is a compile-time constant, which is not guaranteed by const.Both are part of the object and function interfaces.

(2) When modifying functions, the constexpr keyword can modify not only variables and pointers, but also functions (including constructors) as compared with the const keyword.Note that when constexpr is used to define an object of a custom class, it requires that the class have a constant constructor, whereas when const is used to define a class object, it does not.

(3) When modifying the pointer, they behave differently.const before the * sign means that the content pointed to by the pointer cannot be modified. const after the * sign means that the pointer cannot be modified; while the constexpr keyword can only be placed before the * sign, and indicates that the content pointed to by the pointer cannot be modified.

2. Constant Expression Functions

(1) Conditions that form the constexpr function

1. The body of the function has only a single return statement (which can be extended by':'or recursion).This is no longer a limitation in C++14.

2. Function body must have return value (C++11 requires not to be a void function, but C++14 is no longer restricted)

3. Functions with non-constant expressions cannot be called inside constexpr functions, which will cause compilation failure.

4. The return return statement expression cannot use functions of non-constant expressions, global data, and must be a constant expression.

(2) Notes

1. A constant expression function must be defined before it can be used.You cannot declare it before defining it after a function call.

2. The constexpr function returns a compile-time constant if all arguments passed in at the time of the call are known at compile time.As long as any argument is unknown at compile time, it operates like a normal function and produces runtime results.

3. A constexpr function becomes a normal function when its composition is not satisfied.The constexpr function can also be applied to both compile-time and run-time contexts (compile-time contexts such as constexpr int a = func_constexpr(x,y).Runtime context such as int a = func_constexpr(x, y)).

Differences between const and constexpr

#include <iostream>
#include <array>

using namespace std;

int g_count = 0;

int normalfunc(int x)
{
    return x;
}

//constexpr Function (that is, when compiled as a constant expression function or as a normal function)
constexpr int func1(int x)
{
    return x + 2;
}

constexpr void func2(){} //Ordinary function, constexpr Must have non void Return value type of
constexpr int func3();   //Notice here that only the declaration, defined in main()After function
//constexpr int func4()    //Compilation failed
//{
//    int x = normalfunc(2); //Non-invoked constexpr Function!
//    return x;
//}

//Power function: for calculation base Of exp pow
constexpr int pow(int base, int exp)
{
    //Version 1: Recursion (a statement)----> C++11 Requires one and only one statement (i.e. return Sentence). 
    //return (exp == 0) ? 1 : base * pow(base, exp - 1);

    //Version 2: Non-recursive (multiple statements) --->C++14 Above
    auto ret = 1;
    for (int i = 0; i < exp; ++i) {
        ret *= base;
    }

    return ret;
}

int main()
{
    //1.1 When modifying variables
    int x = 0;                 //wrong constexpr variable
    const int y = 0;
    constexpr int a = 2;       //a For compile-time constants
    constexpr int b = a + 4;   //b For compile-time constants
    constexpr int c = y + 1;   //c For compile-time constants, y They are taken directly from the symbol table, not from memory values.

    //constexpr auto m = x;    //error,because x Not Constant
    const auto n = x;          //ok, d by x Copy

    constexpr auto N = 10;
    //std::array<int, n> arr1; //error, n Not a compile-time constant
    std::array<int, N> arr2;   //ok, N For compile-time constants
    std::array<int, c> arr3;   //ok, c For compile-time constants  

    constexpr auto exp = 5;
    std::array<int, pow(3, exp)> arr4;   //ok! pow yes constexpr function
    //std::array<int, pow(3, x)> arr5;   //error! x Is a run-time variable, resulting in pow Return value is run-time value

    //1.2 constexpr When modifying a pointer, it means that the pointer itself cannot be modified!!!( constexpr Can only be placed in*Left side of)
    constexpr int* ptr2 = &g_count; //ok,Global variable, compile-time address can be determined
    //ptr2 = nullptr;     //Note that with const Different, this means ptr2 Is a constant.
    *ptr2 = 100;

    //constexpr int* ptr1 = &x;  //error,because x Assigned on the stack, its address cannot be determined at compile time.

    //1.3 When modifying a function
    int var1 = func3();               //ok! func3 First declare, then define.
    //constexpr int var2 = func3();   //error,Before calling a function, func3 It must be defined (you cannot just see the declaration).

    constexpr int c1 = func1(N);      //constexpr Context: Return Value Assignment constexpr Variable, yes constexpr Function (arguments are also constexpr). 
    int c2 = func1(x);                //wrong constexpr Context: func1 Use as a normal function when return values are assigned to ordinary variables!
    int c3 = func1(N);                //wrong constexpr Context: func1 Use as a normal function when return values are assigned to ordinary variables!

    return 0;
}

constexpr int func3() //Declarations placed in main Before, here's the definition!
{
    return 0;
}

3. Application of constexpr

(1) a constexpr object defining a custom class.

1. The constructor of a custom class must be a constexpr function.

2. constexpr cannot be used to modify virtual functions.Because virtual is a runtime behavior, it conflicts with the meaning of constexpr.

3. The function declared as a constexpr member in C++ 11 is automatically set to a const function, but it is no longer automatically set to a const function in C++14.

(2) Function template: Whether the instantiated function is a constexpr function or not is uncertain at compile time, depending on whether the incoming argument is of type constexpr.

(3) Function recursion of type constexpr

1. The way to program compile-time operations with constexpr is called constexpr metaprogramming

2. Programming based on template compile-time operations is called template metaprogramming.

Application of constexpr

#include <iostream>
using namespace std;

//1. constexpr Constructors and Member Functions
class Point
{
    double x, y;
public:
    //Constant Expression Constructor
    constexpr Point(double x=0, double y=0) noexcept : x(x),y(y){}

    //Member functions (note: constexpr Not allowed for decoration virtual Function)
    constexpr double getX() const noexcept { return x;} //constexpr Function, in C++11 Default in const Function.but C++14 Canceled in, add here const
    constexpr double getY() const noexcept { return y;}

    //The following two functions are used in the C++11 Cannot be declared as constexpr(C++14 Yes!), for the following reasons:
    //1.C++11 Declared as constexpr The member function is automatically set to const Function, and these two functions need to change the value of the member variable, which is const Member function
    //However, it is not allowed to change the value of a member, resulting in compilation errors. C++14 in constexpr Function no longer defaults to const Function, so you can modify member variables.
    //2. C++11 in constexpr Function cannot return void Type, but C++14 No more restrictions.
    constexpr void setX(double newX) noexcept { x = newX; } //C++11 Medium must be removed constexpr;
    constexpr void setY(double newY) noexcept { y = newY; }
};

//Calculate midpoint coordinates
constexpr Point midpoint(const Point& p1, const Point& p2) noexcept
{
    //p1 and p2 Both are const Object, will call const Version of getX()and getY()
    return { (p1.getX() + p2.getX()) / 2, (p1.getY() + p2.getY()) / 2 };
}

//Return p The central symmetric point relative to the origin ( C++14)
constexpr Point reflection(const Point& p) noexcept
{
    Point ret;
    ret.setX(-p.getX());
    ret.setY(-p.getY());
    return ret;
}

//2. Function template: ( constexpr Metaprogramming)
//Is the function constexpr Function, compile time unknown.Depends on the argument passed in t Is it constexpr variable/object
template<typename T>
constexpr T func(T t) 
{
    return t;
}

struct NotLiteral  
{
    int i;
    NotLiteral():i(5){} //Note: The constructor is not constexpr Type!
};

//3.constexpr Recursive function of type
//3.1 Requiponacci Number
constexpr int Fib(int n)
{
    return (n == 1) ? 1 : ((n == 2) ? 1 : Fib(n-1) + Fib(n -2));
}

//3.2 Template Recursion (Template Metaprogramming)
template<long N>
struct Fibonacci
{
    static const long val = Fibonacci<N - 1>::val + Fibonacci<N - 2>::val;
};

//Specialization
template<> struct Fibonacci<2> { static const long val = 1; };
template<> struct Fibonacci<1> { static const long val = 1; };
template<> struct Fibonacci<0> { static const long val = 0; };

void printArray(int a[], int len)
{
    cout << "Fibonacci:  ";
    for (int i=0; i<len; ++i)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}

int main()
{
    //1. use constexpr Object defining a custom class (requires that the class have constexpr Constructor)
    constexpr Point p1(9.4, 27.7);    //ok, p1 For compile-time constants
    constexpr Point p2{ 28.8, 5.3 };  //ok,Ditto
    //1.1 Midpoint
    constexpr auto mid = midpoint(p1, p2);  //mid For compile-time constants, mid.getX()Compile-time constant, too!!!
    //1.2 Find Symmetric Point About Origin
    constexpr auto reflectedMid = reflection(mid);

    //2. constexpr Function template of type
    NotLiteral n1;             //wrong constexpr object
    NotLiteral n2 = func(n1);  //The incoming argument is not constexpr Object, func Become a normal function!
    //constexpr NotLiteral n3 = func(n1); //error,Because NotLiteral The constructor is not constexpr Type,
                                          //Out-of-service constexpr Define the type of constexpr Object!!!
    constexpr int a = func(1); //ok,1 by constexpr object

    //3. constexpr Type Recursive Function
    int fib1[]{ Fib(11), Fib(12), Fib(13), Fib(14) };
    printArray(fib1, 4);

    int fib2[] = { Fibonacci<11>::val, Fibonacci<12>::val, Fibonacci<13>::val, Fibonacci<14>::val };
    printArray(fib1, 4);

    return 0;
}
/*Output Results
Fibonacci:  89 144 233 377
Fibonacci:  89 144 233 377
*/

Posted by MBK on Sun, 28 Jul 2019 10:45:40 -0700