【问题标题】:What are 'constexpr' useful for?'constexpr' 有什么用?
【发布时间】:2015-02-12 22:39:22
【问题描述】:

我真的找不到它的任何用途。我的第一个想法是,我可以用它来实现“契约式设计”,而无需使用这样的宏:

struct S
{   
    S(constexpr int i) : S(i) { static_assert( i < 9, "i must be < 9" ); }

    S(int i); //external defintion

    char *pSomeMemory;
};

但这不会编译。我想我们也可以使用它来引用相同的变量,而无需创建额外的内存,当我们想要避免使用 get/setter 以使来自用户的一个成员的实例为只读时:

class S
{  
private:
    int _i;

public:
    const int & constexpr i = _i;
};

但以上都没有实际编译。谁能告诉我为什么要引入这个关键字?

【问题讨论】:

  • This reference (cppreference.com) 应该是一个好的开始,因为它甚至有一个示例
  • 它们旨在替代预处理器宏的一些更常见用途。
  • 您需要阅读基本参考,上面的链接会有所帮助,阅读constexpr tag中的一些问题也可能会有所帮助
  • 你可以用它们做各种奇怪的事情。 crazycpp.wordpress.com/2014/10/17/…

标签: c++ c++11 language-lawyer compile-time


【解决方案1】:

constexpr 的目标取决于上下文:

  1. 对于对象,它表示该对象是不可变的,应在编译时构造。除了将操作移动到编译时而不是在运行时执行它们之外,创建constexpr 对象还有一个额外的优势,即它们在创建任何线程之前被初始化。因此,他们的访问永远不需要任何同步。将对象声明为 constexpr 的示例如下所示:

    constexpr T value{args};
    

    显然,要使其工作,args 需要是常量表达式。

  2. 对于函数,它表示调用该函数可以产生一个常量表达式。 constexpr 函数调用的结果是否导致常量表达式取决于函数的参数和定义。直接的含义是该函数必须是inline(它将隐含地这样做)。此外,在这样的功能中可以做什么是有限制的。对于 C++11,该函数只能有一个语句,对于非构造函数,该语句必须是 return-statement。这个限制在 C++14 中得到了放松。比如下面是一个constexpr函数的定义:

    constexpr int square(int value) { return value * value; }
    

当创建非内置类型的constexpr 对象时,各个类型将需要constexpr 构造函数:生成的默认构造函数将不起作用。显然,constexpr 构造函数需要初始化所有成员。 constexpr 构造函数可能如下所示:

struct example {
    int value;
    constexpr example(int value): value(value) {}
};

int main() {
    constexpr example size{17};
    int array[size.value] = {};
}

创建的constexpr 值可用于任何需要常量表达式的地方。

【讨论】:

  • 不,对于函数,它表明函数 call 是一个常量表达式,当调用替换这样说时。 (参数可以是常量表达式,但函数调用不一定是。)
【解决方案2】:

这里是 Alex Alllain 在他的“Consexpr - C++11 中的广义常量表达式”中提出的观点摘要,其中详细说明了constexpr 的用处:

  • 首先,使用constexpr 说明符,函数或变量的值可以在编译时。
  • constexpr 说明符的另一个好处是它可以用函数替换宏
  • constexpr 也将有利于您的模板元编程。

有利于效率:

常量表达式...允许在编译时进行某些计算,即在您的代码编译时而不是在程序本身运行时进行。 (阿兰 2)

性能优势:如果某事可以在编译时完成,它将只完成一次,而不是每次程序运行时都完成

其他好处:

然后可以在只允许编译时常量表达式的情况下使用此类变量和函数。对象声明中使用的 constexpr 说明符意味着 const。函数声明中使用的 constexpr 说明符意味着内联。(CPP 1)

constexpr 函数的规则:

  1. 它必须由单个 return 语句组成(有一些例外)
  2. 只能调用其他 constexpr 函数
  3. 它只能引用 constexpr 全局变量 (Allain 6)

constexpr 构造函数规则:

  1. 它的每个参数都必须是字面量类型
  2. 该类必须没有虚拟基类
  3. 构造函数不能有函数尝试块 (CPP 6)

引用

Allain, Alex,“Consexpr - C++11 中的通用常量表达式”,未指定日期,“http://www.cprogramming.com/c++11/c++11-compile-time-processing-with-constexpr.html

CPP,“Consexpr 说明符”,2014 年 12 月 16 日,http://en.cppreference.com/w/cpp/language/constexpr


编辑:对不起,让我觉得我是这些观点的作者是我的错,所以我已经更正了自己,更改了各个部分,并添加了引用以避免抄袭。

【讨论】:

  • 所以它们可以在编译时进行评估?不错的补充,毫无疑问 - 编译器从来没有想过可以在编译时评估某些函数。非常聪明。
  • @Jako:编译器知道许多函数实际上是常量。但是,结果不能用于根据语言需要常量表达式的上下文,例如,在定义枚举器的值、内置数组的大小或非类型模板参数时。将函数和对象声明为 constexpr 使得它们的值可以在这些上下文中使用。
  • 考虑重新格式化您的帖子以表明您直接引用了其他人。就目前而言,它的大部分内容看起来好像您是作者,而事实上它在外部链接中是逐字逐句的。有些人可能会认为这是抄袭。
  • @MickyDuncan 没有引文是我的错,而且似乎我是作者,所以我添加了引文并重新编写和重新格式化了我的帖子。我希望这能解决问题。
【解决方案3】:

在我看来,constexpr 是一种将两种 C++ 语言结合在一起的方式——一种在运行时运行,另一种在编译时运行。编译时编程通常称为元编程。

首先是 C 及其宏。宏实际上是编译器运行的小程序。他们有 if 语句(称为#ifdef),变量(#define)。甚至还有一个完整的scripting language 在编译时运行。

当 C++ 出现时,它只有 C 宏,仅此而已。然后是 C++ 模板。这些引入了一种运行编译时代码的不同方式。 C++ 元语言在很大程度上是功能性的,例如,您可以使用 loops with tail recursion

在 C++ 11 中,他们认为元编程看起来会更好,所以他们引入了constexpr。现在您可以编写也是元函数的 C++ 函数。在 C++ 14 中它变得更好了,因为对 constexpr 函数的限制已经放宽了。

【讨论】:

    【解决方案4】:

    回答“有人可以告诉我为什么要引入 constexpr 关键字吗?”

    现代 C++ 支持两种类型的不变性。

    1) 常量

    2) 常量表达式。

    constexper 将在编译时进行评估。它用于指定常量并允许将数据放置在可能损坏的内存中。

    示例 1:

    void UseConstExpr(int temp)
    {
        // This code snippet is OK
        constexpr int y = max + 300;
        std::cout << y << std::endl;
        // This code snippet gives compilation error
        // [ expression must have a constant value]
        constexpr int z = temp + 300;
    
    }
    

    示例 2:

    int addVector(const std::vector<int>& inVect)
    {
        int sum = 0;
        for ( auto& vec : inVect)
        {
            sum += vec;
        }
        std::cout << sum << std::endl;
        return sum;
    }
    
    int main()
    {
        // vInt is not constant
        std::vector<int> vInt = { 1,2,3,4,5,6 }; 
        // This code snippet is OK
        // because evaluated at run time
        const int iResult = addVector(vInt);
        // Compiler throws below error
        // function call must have a constant value in a constant expression
        // because addVector(vInt) function is not a constant expression
        constexpr int iResult = addVector(vInt);
        return 0;
    }
    

    注意:以上源码是在VS2015上编译的

    【讨论】:

      猜你喜欢
      • 2020-02-28
      • 1970-01-01
      • 2023-01-23
      • 2020-12-24
      • 2019-04-22
      • 1970-01-01
      • 2021-02-20
      • 1970-01-01
      • 2020-05-11
      相关资源
      最近更新 更多