精华内容
下载资源
问答
  • 所谓常量表达式的就是由多个(≥1)常量组成的表达式。换句话说,如果表达式中的成员都是常量,那么该表达式就是一个常量表达式。这也意味着,常量表达式一旦确定,其值将无法修改。实际开发中,我们经常会用到...

    constexpr 是 C++ 11 标准新引入的关键字,不过在讲解其具体用法和功能之前,读者需要先搞清楚 C++ 常量表达式的含义。

    所谓常量表达式,指的就是由多个(≥1)常量组成的表达式。换句话说,如果表达式中的成员都是常量,那么该表达式就是一个常量表达式。这也意味着,常量表达式一旦确定,其值将无法修改。

    实际开发中,我们经常会用到常量表达式。以定义数组为例,数组的长度就必须是一个常量表达式:

    // 1)

    int url[10];//正确

    // 2)

    int url[6 + 4];//正确

    // 3)

    int length = 6;

    int url[length];//错误,length是变量

    上述代码演示了 3 种定义 url 数组的方式,其中第 1、2 种定义 url 数组时,长度分别为 10 和 6+4,显然它们都是常量表达式,可以用于表示数组的长度;第 3 种 url 数组的长度为 length,它是变量而非常量,因此不是一个常量表达式,无法用于表示数组的长度。

    常量表达式的应用场景还有很多,比如匿名枚举、switch-case 结构中的 case 表达式等,感兴趣的读者可自行编码测试,这里不再过多举例。

    我们知道,C++ 程序的执行过程大致要经历编译、链接、运行这 3 个阶段。值得一提的是,常量表达式和非常量表达式的计算时机不同,非常量表达式只能在程序运行阶段计算出结果;而常量表达式的计算往往发生在程序的编译阶段,这可以极大提高程序的执行效率,因为表达式只需要在编译阶段计算一次,节省了每次程序运行时都需要计算一次的时间。

    对于用 C++ 编写的程序,性能往往是永恒的追求。那么在实际开发中,如何才能判定一个表达式是否为常量表达式,进而获得在编译阶段即可执行的“特权”呢?除了人为判定外,C++11 标准还提供有 constexpr 关键字。

    constexpr 关键字的功能是使指定的常量表达式获得在程序编译阶段计算出结果的能力,而不必等到程序运行阶段。C++ 11 标准中,constexpr 可用于修饰普通变量、函数(包括模板函数)以及类的构造函数。

    注意,获得在编译阶段计算出结果的能力,并不代表 constexpr 修饰的表达式一定会在程序编译阶段被执行,具体的计算时机还是编译器说了算。

    constexpr修饰普通变量

    C++11 标准中,定义变量时可以用 constexpr 修饰,从而使该变量获得在编译阶段即可计算出结果的能力。

    值得一提的是,使用 constexpr 修改普通变量时,变量必须经过初始化且初始值必须是一个常量表达式。举个例子:

    #include

    using namespace std;

    int main()

    {

    constexpr int num = 1 + 2 + 3;

    int url[num] = {1,2,3,4,5,6};

    couts<< url[1] << endl;

    return 0;

    }

    程序执行结果为:

    2

    读者可尝试将 constexpr 删除,此时编译器会提示“url[num] 定义中 num 不可用作常量”。

    可以看到,程序第 6 行使用 constexpr 修饰 num 变量,同时将 "1+2+3" 这个常量表达式赋值给 num。由此,编译器就可以在编译时期对 num 这个表达式进行计算,因为 num 可以作为定义数组时的长度。

    有读者可能发现,将此示例程序中的 constexpr 用 const 关键字替换也可以正常执行,这是因为 num 的定义同时满足“num 是 const 常量且使用常量表达式为其初始化”这 2 个条件,由此编译器会认定 num 是一个常量表达式。

    注意,const 和 constexpr 并不相同,关于它们的区别,我们会在下一节做详细讲解。

    另外需要重点提出的是,当常量表达式中包含浮点数时,考虑到程序编译和运行所在的系统环境可能不同,常量表达式在编译阶段和运行阶段计算出的结果精度很可能会受到影响,因此 C++11 标准规定,浮点常量表达式在编译阶段计算的精度要至少等于(或者高于)运行阶段计算出的精度。

    constexpr修饰函数

    constexpr 还可以用于修饰函数的返回值,这样的函数又称为“常量表达式函数”。

    注意,constexpr 并非可以修改任意函数的返回值。换句话说,一个函数要想成为常量表达式函数,必须满足如下 4 个条件。

    1) 整个函数的函数体中,除了可以包含 using 指令、typedef 语句以及 static_assert 断言外,只能包含一条 return 返回语句。

    举个例子:

    constexpr int display(int x) {

    int ret = 1 + 2 + x;

    return ret;

    }

    注意,这个函数是无法通过编译的,因为该函数的返回值用 constexpr 修饰,但函数内部包含多条语句。

    如下是正确的定义 display() 常量表达式函数的写法:

    constexpr int display(int x) {

    //可以添加 using 执行、typedef 语句以及 static_assert 断言

    return 1 + 2 + x;

    }

    可以看到,display() 函数的返回值是用 constexpr 修饰的 int 类型值,且该函数的函数体中只包含一个 return 语句。

    2) 该函数必须有返回值,即函数的返回值类型不能是 void。

    举个例子:

    constexpr void display() {

    //函数体

    }

    像上面这样定义的返回值类型为 void 的函数,不属于常量表达式函数。原因很简单,因为通过类似的函数根本无法获得一个常量。

    3) 函数在使用之前,必须有对应的定义语句。我们知道,函数的使用分为“声明”和“定义”两部分,普通的函数调用只需要提前写好该函数的声明部分即可(函数的定义部分可以放在调用位置之后甚至其它文件中),但常量表达式函数在使用前,必须要有该函数的定义。

    举个例子:

    #include

    using namespace std;

    //普通函数的声明

    int noconst_dis(int x);

    //常量表达式函数的声明

    constexpr int display(int x);

    //常量表达式函数的定义

    constexpr int display(int x){

    return 1 + 2 + x;

    }

    int main()

    {

    //调用常量表达式函数

    int a[display(3)] = { 1,2,3,4 };

    cout << a[2] << endl;

    //调用普通函数

    cout << noconst_dis(3) << endl;

    return 0;

    }

    //普通函数的定义

    int noconst_dis(int x) {

    return 1 + 2 + x;

    }

    程序执行结果为:

    3

    6

    读者可自行将 display() 常量表达式函数的定义调整到 main() 函数之后,查看编译器的报错信息。

    可以看到,普通函数在调用时,只需要保证调用位置之前有相应的声明即可;而常量表达式函数则不同,调用位置之前必须要有该函数的定义,否则会导致程序编译失败。

    4) return 返回的表达式必须是常量表达式,举个例子:

    #include

    using namespace std;

    int num = 3;

    constexpr int display(int x){

    return num + x;

    }

    int main()

    {

    //调用常量表达式函数

    int a[display(3)] = { 1,2,3,4 };

    return 0;

    }

    该程序无法通过编译,编译器报“display(3) 的结果不是常量”的异常。

    常量表达式函数的返回值必须是常量表达式的原因很简单,如果想在程序编译阶段获得某个函数返回的常量,则该函数的 return 语句中就不能包含程序运行阶段才能确定值的变量。

    注意,在常量表达式函数的 return 语句中,不能包含赋值的操作(例如 return x=1 在常量表达式函数中不允许的)。另外,用 constexpr 修改函数时,函数本身也是支持递归的,感兴趣的读者可自行尝试编码测试。

    constexpr修饰类的构造函数

    对于 C++ 内置类型的数据,可以直接用 constexpr 修饰,但如果是自定义的数据类型(用 struct 或者 class 实现),直接用 constexpr 修饰是不行的。

    举个例子:

    #include

    using namespace std;

    //自定义类型的定义

    constexpr struct myType {

    const char* name;

    int age;

    //其它结构体成员

    };

    int main()

    {

    constexpr struct myType mt { "zhangsan", 10 };

    cout << mt.name << " " << mt.age << endl;

    return 0;

    }

    此程序是无法通过编译的,编译器会抛出“constexpr不能修饰自定义类型”的异常。

    当我们想自定义一个可产生常量的类型时,正确的做法是在该类型的内部添加一个常量构造函数。例如,修改上面的错误示例如下:

    #include

    using namespace std;

    //自定义类型的定义

    struct myType {

    constexpr myType(char *name,int age):name(name),age(age){};

    const char* name;

    int age;

    //其它结构体成员

    };

    int main()

    {

    constexpr struct myType mt { "zhangsan", 10 };

    cout << mt.name << " " << mt.age << endl;

    return 0;

    }

    程序执行结果为:

    zhangsan 10

    可以看到,在 myType 结构体中自定义有一个构造函数,借助此函数,用 constexpr 修饰的 myType 类型的 my 常量即可通过编译。

    注意,constexpr 修饰类的构造函数时,要求该构造函数的函数体必须为空,且采用初始化列表的方式为各个成员赋值时,必须使用常量表达式。

    前面提到,constexpr 可用于修饰函数,而类中的成员方法完全可以看做是“位于类这个命名空间中的函数”,所以 constexpr 也可以修饰类中的成员函数,只不过此函数必须满足前面提到的 4 个条件。

    举个例子:

    #include

    using namespace std;

    //自定义类型的定义

    class myType {

    public:

    constexpr myType(const char *name,int age):name(name),age(age){};

    constexpr const char * getname(){

    return name;

    }

    constexpr int getage(){

    return age;

    }

    private:

    const char* name;

    int age;

    //其它结构体成员

    };

    int main()

    {

    constexpr struct myType mt { "zhangsan", 10 };

    constexpr const char * name = mt.getname();

    constexpr int age = mt.getage();

    cout << name << " " << age << endl;

    return 0;

    }

    程序执行结果为:

    zhangsan 10

    注意,C++11 标准中,不支持用 constexpr 修饰带有 virtual 的成员方法。

    constexpr修饰模板函数

    C++11 语法中,constexpr 可以修饰模板函数,但由于模板中类型的不确定性,因此模板函数实例化后的函数是否符合常量表达式函数的要求也是不确定的。

    针对这种情况下,C++11 标准规定,如果 constexpr 修饰的模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略,即该函数就等同于一个普通函数。

    举个例子:

    #include

    using namespace std;

    //自定义类型的定义

    struct myType {

    const char* name;

    int age;

    //其它结构体成员

    };

    //模板函数

    template

    constexpr T dispaly(T t){

    return t;

    }

    int main()

    {

    struct myType stu{"zhangsan",10};

    //普通函数

    struct myType ret = dispaly(stu);

    cout << ret.name << " " << ret.age << endl;

    //常量表达式函数

    constexpr int ret1 = dispaly(10);

    cout << ret1 << endl;

    return 0;

    }

    程序执行结果为:

    zhangsan 10

    10

    可以看到,示例程序中定义了一个模板函数 display(),但由于其返回值类型未定,因此在实例化之前无法判断其是否符合常量表达式函数的要求:

    第 20 行代码处,当模板函数中以自定义结构体 myType 类型进行实例化时,由于该结构体中没有定义常量表达式构造函数,所以实例化后的函数不是常量表达式函数,此时 constexpr 是无效的;

    第 23 行代码处,模板函数的类型 T 为 int 类型,实例化后的函数符合常量表达式函数的要求,所以该函数的返回值就是一个常量表达式。

    展开全文
  • 常量表达式

    万次阅读 2017-11-20 22:51:42
    常量表达式(const experssion):是(1)值不会改变 并且 (2)在编译过程就能得到计算结果的表达式。字面量属于常量表达式,用常量表达式初始化的const对象也是常量表达式。 一个对象(或表达式)是不

    字面值:是一个不能改变的值,如数字、字符、字符串等。单引号内的是字符字面值,双引号内的是字符串字面值。

    字面值类型(literal type):算数类型、引用和指针等。


    常量表达式(const experssion):是指(1)值不会改变 并且 (2)在编译过程就能得到计算结果的表达式。字面量属于常量表达式,用常量表达式初始化的const对象也是常量表达式。

    一个对象(或表达式)是不是常量表达式由它的数据类型和初始值共同决定。

    const int a =1;		//常量表达式
    cosnt int b=a+1;	//常量表达式
    int c=2;		//初始值是字面值常量,当c数据类型是普通int。
    const int d=fun();	//fun()值要在运行时得到,d不是字面值常量。

    constexpr变量

    C++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的是否是一个常量表达式。

    声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。


    constexpr函数

    constexpr函数是指能用于常量表达式的函数。

    应遵循的约定:函数返回类型及所有形参的类型都是字面值类型,而且函数体中必须有且只有一条return 语句。

    constexpr函数体内可以有其他语句,只要这些语句在运行时不执行任何操作。(例如,空语句、类型别名和using声明等)

    constexpr函数被隐式地指定为内联函数。

    constexpr int fun(int a){ return a*10; }
    若a是常量表达式,fun(a)就是常量表达式。若a不是常量表达式,fun(a)就是变成普通函数。

    当constexpr函数是常量表达式时可以用来初始化constexpr变量。


    constexpr和指针

    constexpr声明中定义了一个指针,限定符constexpr仅对指针对象有效,与指针所指的对象无关。
    const int *p1=nullptr;		//p1是指向整形常量的指针
    constexpr int *p2=nullptr;	//p2是指向整形的常量指针
    (等效于 int *const p2=nullptr;)
    但constexpr声明的指针初始值只能是0、nullptr和存储于某个固定地址中的对象。因为它是常量表达式(常量表达式定义(2))。




    展开全文
  • constexpr和常量表达式

    2019-08-19 23:17:00
    常量表达式不会改变且在编译过程中就能计算出来的表达式。 字面值是常量表达式,用常量表达式初始化的const对象也是常量表达式。 一个对象或表达式是不是常量表达是由它的数据类型和初始值共同决定。 例: const...

    constexpr和常量表达式

    常量表达式

    常量表达式是指不会改变且在编译过程中就能计算出来的表达式。
    字面值是常量表达式,用常量表达式初始化的const对象也是常量表达式。
    一个对象或表达式是不是常量表达是由它的数据类型和初始值共同决定。

    例:

    const int a = 10;//常量表达式
    const int b = a + 5;//常量表达式
    int c = 3;//不是常量表达式
    const int d = f();//不是常量表达式
    constexpr int *p = &c;//p是一个常量指针,指针p不可变
    const int *p1 = &c;//错误,p1需要指向一个常量
    

    说明:虽然c有字面值(常量表达式)初始化,但它自己并不是const的,也就是在程序运行时是可变的,因此不是常量表达式,d虽然是const的,但是由于它在程序运行时才能知道是多少,不是由常量表达式对其初始化,故也不是常量表达式。

    C++的标准规定,可以将变量声明为constexpr类型,这样在编译的时候编译器就会检查变量的值是否是一个常量表达式。

    constexpr函数:明天再记录

    展开全文
  • 常量表达式值不会改变并且在编译过程中就能得到计算结果的表达式,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式 const int max = 20;//常量表达式 const int limit = max + 1;//常量...

    constexpr和常量表达式


    常量表达式

    常量表达式是指值不会改变并且在编译过程中就能得到计算结果的表达式,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式

    const int max = 20;//常量表达式
    const int limit = max + 1;//常量表达式
    int staff = 10;//不是常量表达式
    

    constexpr

    在一个复杂系统中,很难分辨一个初始值到底是不是常量表达式

    C++11新标准规定,允许将变量声明为constexpr类型以便编译器来验证变量的值是否是一个常量表达式,声明为constexpr的变量一定是一个常量,且必须用常量表达式来初始化

    constexpr int r1 = 0;
    constexpr int limit_tmp = r1 + 1;
    

    指针和constexpr

    const int* p = nullptr;//p是一个指向整数常量的指针
    constexpr int* q = nullptr;//q是一个指向整数的常量指针
    

    constexpr把它所定义的对象设置成了顶层const

    展开全文
  • 常量表达式函数

    2019-07-13 11:06:47
    我们可以在函数返回类型前加入关键字constexpr来使其成为常量表达式函数,但并非所有的函数都有资格成为常量表达式函数。事实上,常量表达式函数的要求非常严格,总结如下: 函数体只有单一的return返回语句。 ...
  • 常量表达式常量表达式的定义: 是值不会改变并且在编译过程中就能得到计算结果的表达式。 常量表达式的种类: 字面值是常量表达式常量表达式初始化的const对象也是常量表达式 常量表达式举例:const int max_...
  • 常量表达式和constexpr

    2021-06-30 11:49:49
    1、常量表达式值不会改变并且在编译过程就能得到计算结果的表达式,如下: const int max_i= 5; //max_i 是常量表达式 int max_i = 5 //max_i不是常量表达式 const int max_i = get_size(); //max_i不是常量...
  • 1. 什么是常量表达式 常量表达式值不会改变并且在编译过程就能得到计算结果的表达式。 显然,字面值属于常量表达式, 用常量表达式初始化的const对象也是常量表达式。 const int maxCount = 10; // 常量...
  • 常量表达式(const expression)是值不会改变并且在编译过程就能得到结果的表达式。 字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。 const int a = 4; //字面值4是常量表达式,a 是一个...
  • 常量表达式值不会改变并且在编译的过程中就能得到结果的表达式。显然,字面值属于常量表达式.... 然后书上关于字面值得解释是这样的: 字面值类型一般比较简单,值也显而易见,容易得到,就把他们称为“字面值...
  • c++ 常量表达式

    2016-05-13 13:20:50
    常量表达式指这种表达式能够在编译时刻被计算。 这种表达式,能被用到non-type模板参数,array sizes, 和其他需要const expression的地方。 int n = 1; std::array a1; // error, n is not a constant expressi
  • 常量表达式概念与用处

    千次阅读 多人点赞 2017-12-30 21:13:13
    1)什么常量表达式?  在编译期间进行求值的表达式。  1、字面值常量是常量表达式;如123,‘a’,3.14等  2、用常量表达式初始化的const对象也是常量表达式。如int const a=5;语句中a就是常量表达式 2)...
  • 1)常量表达式:在“编译过程”就能确定结果的表达式。 包括: 字面值 常量表达式初始化的const对象 以下不是常量表达式 int s = 123; const int sz = get_size(); 2)constexpr变量(C++11): 变量声明为contexpr类型...
  • 常量表达式(constexpression)是值不会改变并且在编译过程就能够得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。后面将会提到,C++语言中有几种情况下是要...
  • C++ 常量表达式 常量值是不会更改的值。C + + 提供了两个关键字,它们使你能够表达不打算修改对象的意图,还可让你实现该意图。 C++ 需要常量表达式(计算结果为常量的表达式)以便声明: 数组边界 case 语句中...
  • 常量表达式不会改变且在编译过程中就能得到计算结果的表达式。 int main() { const int value1 = 10; const int value2 = value1 + 1; return 0; } constexpr constexpr和const很像,有如下区别 使用...
  • 1.常量表达式 const int max = 100; // 常量表达式 const int limit = max +1; // 常量表达式 const int size = getSize(); // 不是常量表达式,因为getSize()运行时才有值 2.constexpr 变量 constexpr 类型:...
  • 常量表达式(constexpr)

    2019-05-21 19:12:00
    那么为什么要用常量表达式呢,用常量表达式会有什么好处:  1.允许一些计算只在编译时进行一次,而不是每次程序运行时;  2.编译器可以进行尺度更大的优化;  3.可以用在需求编译期间常量的上下文,例如数组...
  • 常量表达式(const expression)是值不会改变并且在编译过程就能得到计算结果的表达式。显然,字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。后面将提到,C++语言有几种情况下是要用到常量...
  • c++常量和常量表达式

    千次阅读 2019-08-03 18:49:31
    const,默认情况下仅在文件内有效 const int i(12); const引用:对常量的引用不能被用作修改它所绑定的对象 ...允许常量引用绑定到非常量的对象、字面值或表达式上 int i = 20; const int &ri(20); con...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 96,322
精华内容 38,528
关键字:

常量表达式是指什么