前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >现代C++之constexpr

现代C++之constexpr

作者头像
公众号guangcity
发布2020-02-13 11:44:26
1.2K0
发布2020-02-13 11:44:26
举报
文章被收录于专栏:光城(guangcity)光城(guangcity)

现代C++之constexpr

constexpr在 C++11 引入、在 C++14 得到大幅改进。

(1)C++11中的constexpr指定的函数返回值和参数必须要保证是字面值,而且必须只有一行return代码,这给函数的设计者带来了更多的限制,比如通常只能通过return 三目运算符+递归来计算返回的字面值。

(2)C++14中只要保证返回值和参数是字面值就行了,函数体中可以加入更多的语句,方便了更灵活的计算。

它的字面意思是 constant expression,常量表达式。

1.变量

constconstexpr可以应用到变量函数。尽管它们彼此相似,但实际上它们是非常不同的概念。

constconstexpr意味着他们的值不能在初始化后改变。因此,例如:

代码语言:javascript
复制
const int x1=10;
constexpr int x2=10;

x1=20; // ERROR. Variable 'x1' can't be changed.
x2=20; // ERROR. Variable 'x2' can't be changed.

constconstexpr之间的主要区别是初始化值的时间(求值)。尽管const变量的值可以在编译时和运行时,但constexpr始终在编译时进行求值。例如:

代码语言:javascript
复制
int temp=rand(); // temp is generated by the the random generator at runtime.

const int x1=10; // OK - known at compile time.
const int x2=temp; // OK - known only at runtime.
constexpr int x3=10; // OK - known at compile time.
constexpr int x4=temp; // ERROR. Compiler can't figure out the value of 'temp' variable at compile time so `constexpr` can't be applied here.

知道在编译时还是运行时知道该值的主要优点是,只要需要编译时间常数,就可以使用编译时间常数。例如,C ++不允许您使用可变长度指定C数组:

代码语言:javascript
复制
int temp=rand(); // temp is generated by the the random generator at runtime.

int array1[10]; // OK.
int array2[temp]; // ERROR.

因此,这意味着:

代码语言:javascript
复制
const int size1=10; // OK - value known at compile time.
const int size2=temp; // OK - value known only at runtime.
constexpr int size3=10; // OK - value known at compile time.


int array3[size1]; // OK - size is known at compile time.
int array4[size2]; // ERROR - size is known only at runtime time.
int array5[size3]; // OK - size is known at compile time.

因此,const变量既可以定义编译时常量(比如size1)来指定数组大小,也可以定义运行时常量(比如size2)来定义数组大小。另一方面,constexpr总是定义可以指定数组大小的编译时常量

2.函数

constconstexpr也可以应用于函数。const函数必须是成员函数(方法,运算符),其中const关键字的应用意味着该方法无法更改其成员(非静态)字段的值。例如。

代码语言:javascript
复制
class test
{
   int x;

   void function1()
   {
      x=100; // OK.
   }

   void function2() const
   {
      x=100; // ERROR. The const methods can't change the values of object fields.
   }
};

constexpr是一个不同的概念。如果将编译时常量作为参数传递,则它将一个函数(成员或非成员)标记为可以在编译时求值的函数。例如,您可以编写此代码。

代码语言:javascript
复制
constexpr int func_constexpr(int X, int Y)
{
    return(X*Y);
}

int func(int X, int Y)
{
    return(X*Y);
}

int array1[func_constexpr(10,20)]; // OK - func_constexpr() can be evaluated at compile time.
int array2[func(10,20)]; // ERROR - func() is not a constexpr function.

int array3[func_constexpr(10,rand())]; // ERROR - even though func_constexpr() is the 'constexpr' function, the expression 'constexpr(10,rand())' can't be evaluated at compile time.

顺便说一下,这些constexpr函数是常规C ++函数,即使传递了非常量参数也可以调用它们。但是在这种情况下,您将获得非constexpr值。

代码语言:javascript
复制
int value1=func_constexpr(10,rand()); // OK. value1 is non-constexpr value that is evaluated in runtime.
constexpr int value2=func_constexpr(10,rand()); // ERROR. value2 is constexpr and the expression func_constexpr(10,rand()) can't be evaluated at compile time.

3.类方法

constexpr也可应用于所述成员函数(方法),操作者和甚至构造函数。例如。

代码语言:javascript
复制
class test2
{
    static constexpr int function(int value)
    {
        return(value+1);
    }

    void f()
    {
        int x[function(10)];


    }
};

一个更“疯狂”的样本。

代码语言:javascript
复制
class test3
{
    public:

    int value;

    // constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
    constexpr int getvalue() const
    {
        return(value);
    }

    constexpr test3(int Value)
        : value(Value)
    {
    }
};


constexpr test3 x(100); // OK. Constructor is constexpr.

int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.

4.建议

Tips from the book Effective Modern C++ by Scott Meyers about constexpr:

  • constexpr 对象是const,在编译期间使用已知的值初始化;
  • constexpr 函数当使用在编译期间已知值的参数调用时,constexpr函数产生编译时结果;
  • 与非constexpr对象和函数相比,constexpr对象和函数可以在更广泛的上下文中使用;
  • constexpr 是对象或函数接口的一部分。

5.补充

内联变量C++17 引入了内联(inline)变量的概念,允许在头文件中定义内联变量,然后像内联函数一样,只要所有的定义都相同,那变量的定义出现多次也没有关系。对于类的静态数据成员,const 缺省是不内联的,而 constexpr 缺省就是内联的。这种区别在你用 & 去取一个 const int 值的地址、或将其传到一个形参类型为 const int& 的函数去的时候(这在 C++ 文档里的行话叫 ODR-use),就会体现出来。

合法例子:

代码语言:javascript
复制
#include <iostream>

struct magic {
  static const int number = 42;
};

int main()
{
  std::cout << magic::number
            << std::endl;
}

不合法例子:

代码语言:javascript
复制
#include <iostream>
#include <vector>

struct magic {
  static const int number = 42;
};

int main()
{
  std::vector<int> v;
  //  调用  push_back(const T&)
  v.push_back(magic::number);
  std::cout << v[0] << std::endl;
}

程序在链接时就会报错了,说找不到 magic::number。这是因为 ODR(下面的one definition rule)-use 的类静态常量也需要有一个定义,在没有内联变量之前需要在某一个源代码文件(非头文件)中这样写:

代码语言:javascript
复制
const int magic::number = 42;

必须正正好好一个,多了少了都不行,所以叫 one definition rule。

内联函数,现在又有了内联变量,以及模板,则不受这条规则限制。修正这个问题的简单方法是把 magic 里的 static const 改成 static constexpr 或 static inline const。前者可行的原因是,类的静态 constexpr 成员变量默认就是内联的。const 常量和类外面的 constexpr 变量不默认内联,需要手工加 inline 关键字才会变成内联。

6.学习资料

https://stackoverflow.com/questions/14116003/difference-between-constexpr-and-const

极客时间现代C++

ODR-use https://zh.cppreference.com/w/cpp/language/definition

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-01-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光城 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 现代C++之constexpr
    • 1.变量
      • 2.函数
        • 3.类方法
          • 4.建议
            • 5.补充
              • 6.学习资料
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档