什么时候应该在 C++11 中使用 constexpr 功能?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4748083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:21:10  来源:igfitidea点击:

When should you use constexpr capability in C++11?

c++c++11constexpr

提问by Warren P

It seems to me that having a "function that always returns 5" is breaking or diluting the meaning of "calling a function". There must be a reason, or a need for this capability or it wouldn't be in C++11. Why is it there?

在我看来,拥有“总是返回 5 的函数”正在破坏或淡化“调用函数”的含义。必须有一个原因,或者需要这种能力,否则 C++11 中就没有。为什么会在那里?

// preprocessor.
#define MEANING_OF_LIFE 42

// constants:
const int MeaningOfLife = 42;

// constexpr-function:
constexpr int MeaningOfLife () { return 42; }

It seems to me that if I wrote a function that return a literal value, and I came up to a code-review, someone would tell me, I should then, declare a constant value instead of writing return 5.

在我看来,如果我编写了一个返回字面值的函数,然后我进行了代码,那么有人会告诉我,我应该声明一个常量值而不是编写 return 5。

采纳答案by Goz

Suppose it does something a little more complicated.

假设它做了一些更复杂的事情。

constexpr int MeaningOfLife ( int a, int b ) { return a * b; }

const int meaningOfLife = MeaningOfLife( 6, 7 );

Now you have something that can be evaluated down to a constant while maintaining good readability and allowing slightly more complex processing than just setting a constant to a number.

现在,您可以将一些内容评估为常数,同时保持良好的可读性,并允许比仅将常数设置为数字更复杂的处理。

It basically provides a good aid to maintainability as it becomes more obvious what you are doing. Take max( a, b )for example:

它基本上为可维护性提供了很好的帮助,因为您正在做的事情变得更加明显。就拿max( a, b )例如:

template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }

Its a pretty simple choice there but it does mean that if you call maxwith constant values it is explicitly calculated at compile time and not at runtime.

它在那里是一个非常简单的选择,但它确实意味着如果您max使用常量值调用它是在编译时而不是在运行时显式计算的。

Another good example would be a DegreesToRadiansfunction. Everyone finds degrees easier to read than radians. While you may know that 180 degrees is in radians it is much clearer written as follows:

另一个很好的例子是DegreesToRadians函数。每个人都发现度数比弧度更容易阅读。虽然您可能知道 180 度以弧度表示,但如下写得更清楚:

const float oneeighty = DegreesToRadians( 180.0f );

Lots of good info here:

这里有很多好信息:

http://en.cppreference.com/w/cpp/language/constexpr

http://en.cppreference.com/w/cpp/language/constexpr

回答by Filip Roséen - refp

Introduction

介绍

constexprwas not introduced as a way to tell the implementation that something can be evaluated in a context which requires a constant-expression; conforming implementations has been able to prove this prior to C++11.

constexpr不是为了告诉实现可以在需要常量表达式的上下文中评估某些内容而引入的;符合标准的实现已经能够在 C++11 之前证明这一点。

Something an implementation cannot prove is the intentof a certain piece of code:

实现无法证明的是某段代码的意图

  • What is it that the developer want to express with this entity?
  • Should we blindly allow code to be used in a constant-expression, just because it happens to work?
  • 开发者想用这个实体表达什么?
  • 我们是否应该盲目地允许在常量表达式中使用代码,仅仅因为它碰巧有效?


What would the world be without constexpr?

没有了世界会constexpr怎样?

Let's say you are developing a library and realize that you want to be able to calculate the sum of every integer in the interval (0,N].

假设您正在开发一个库并意识到您希望能够计算 interval 中每个整数的总和(0,N]

int f (int n) {
  return n > 0 ? n + f (n-1) : n;
}

The lack of intent

缺乏意图

A compiler can easily prove that the above function is callable in a constant-expressionif the argument passed is known during translation; but you have not declared this as an intent - it just happened to be the case.

如果在翻译过程中传递的参数是已知的,编译器可以很容易地证明上述函数在常量表达式中是可调用的;但是您没有将此声明为意图 - 恰好是这种情况。

Now someone else comes along, reads your function, does the same analysis as the compiler; "Oh, this function is usable in a constant-expression!", and writes the following piece of code.

现在其他人来了,读取你的函数,做与编译器相同的分析;“哦,这个函数可以用在常量表达式中!” ,并编写以下代码。

T arr[f(10)]; // freakin' magic

The optimization

优化

You, as an "awesome"library developer, decide that fshould cache the result when being invoked; who would want to calculate the same set of values over and over?

作为“了不起”的库开发人员,您决定f在调用时缓存结果;谁会想要一遍又一遍地计算相同的一组值?

int func (int n) { 
  static std::map<int, int> _cached;

  if (_cached.find (n) == _cached.end ()) 
    _cached[n] = n > 0 ? n + func (n-1) : n;

  return _cached[n];
}

The result

结果

By introducing your silly optimization, you just broke every usage of your function that happened to be in a context where a constant-expressionwas required.

通过引入你愚蠢的优化,你只是破坏了碰巧在需要常量表达式的上下文中的函数的每次使用。

You never promised that the function was usable in a constant-expression, and without constexprthere would be no way of providing such promise.

您从未承诺过该函数可用于constant-expression,否则constexpr将无法提供此类承诺。



So, why do we need constexpr?

那么,我们为什么需要constexpr

The primary usage of constexpris to declare intent.

constexpr的主要用途是声明意图

If an entity isn't marked as constexpr- it was never intended to be used in a constant-expression; and even if it is, we rely on the compiler to diagnose such context (because it disregards our intent).

如果实体未标记为constexpr- 它从未打算在常量表达式中使用;即使是这样,我们也依赖编译器来诊断这种上下文(因为它无视我们的意图)。

回答by Konrad Rudolph

Take std::numeric_limits<T>::max(): for whatever reason, this is a method. constexprwould be beneficial here.

Take std::numeric_limits<T>::max():无论出于何种原因,这是一种方法。constexpr在这里会有好处。

Another example: you want to declare a C-array (or a std::array) that is as big as another array. The way to do this at the moment is like so:

另一个示例:您想声明一个std::array与另一个数组一样大的 C 数组(或 a )。目前这样做的方法是这样的:

int x[10];
int y[sizeof x / sizeof x[0]];

But wouldn't it be better to be able to write:

但是,能够这样写不是更好吗:

int y[size_of(x)];

Thanks to constexpr, you can:

感谢constexpr,您可以:

template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
    return N;
}

回答by deft_code

constexprfunctions are really nice and a great addition to c++. However, you are right in that most of the problems it solves can be inelegantly worked around with macros.

constexpr函数真的很好,是对 C++ 的一个很好的补充。但是,您是对的,因为它解决的大多数问题都可以用宏来处理。

However, one of the uses of constexprhas no C++03 equivalent, typed constants.

但是, 的用途之一constexpr没有 C++03 等效的类型常量。

// This is bad for obvious reasons.
#define ONE 1;

// This works most of the time but isn't fully typed.
enum { TWO = 2 };

// This doesn't compile
enum { pi = 3.1415f };

// This is a file local lvalue masquerading as a global
// rvalue.  It works most of the time.  But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;

// This is a true constant rvalue
constexpr float pi = 3.1415f;

// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor

struct A
{
   static const int four = 4;
   static const int five = 5;
   constexpr int six = 6;
};

int main()
{
   &A::four; // linker error
   &A::six; // compiler error

   // EXTREMELY subtle linker error
   int i = rand()? A::four: A::five;
   // It not safe use static const class variables with the ternary operator!
}

//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;

回答by luke

From what I've read, the need for constexpr comes from an issue in metaprogramming. Trait classes may have constants represented as functions, think: numeric_limits::max(). With constexpr, those types of functions can be used in metaprogramming, or as array bounds, etc etc.

据我所知,对 constexpr 的需求来自元编程中的一个问题。Trait 类可能将常量表示为函数,想想:numeric_limits::max()。使用 constexpr,这些类型的函数可用于元编程,或用作数组边界等。

Another example off of the top of my head would be that for class interfaces, you may want derived types define their own constants for some operation.

我脑子里的另一个例子是对于类接口,您可能希望派生类型为某些操作定义自己的常量。

Edit:

编辑:

After poking around on SO, it looks like others have come up with someexamplesof what might be possible with constexprs.

在仔细研究了 SO 之后,其他人似乎已经提出了一些使用 constexprs 可能实现的示例

回答by luke

From Stroustrup's speech at "Going Native 2012":

来自 Stroustrup 在“Going Native 2012”上的演讲:

template<int M, int K, int S> struct Unit { // a unit in the MKS system
       enum { m=M, kg=K, s=S };
};

template<typename Unit> // a magnitude with a unit 
struct Value {
       double val;   // the magnitude 
       explicit Value(double d) : val(d) {} // construct a Value from a double 
};

using Speed = Value<Unit<1,0,-1>>;  // meters/second type
using Acceleration = Value<Unit<1,0,-2>>;  // meters/second/second type
using Second = Unit<0,0,1>;  // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second 

constexpr Value<Second> operator"" s(long double d)
   // a f-p literal suffixed by ‘s'
{
  return Value<Second> (d);  
}   

constexpr Value<Second2> operator"" s2(long double d)
  // a f-p literal  suffixed by ‘s2' 
{
  return Value<Second2> (d); 
}

Speed sp1 = 100m/9.8s; // very fast for a human 
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)  
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit) 
Acceleration acc = sp1/0.5s; // too fast for a human

回答by Motti

Another use (not yet mentioned) is constexprconstructors. This allows creating compile time constants which don't have to be initialized during runtime.

另一个用途(尚未提及)是constexpr构造函数。这允许创建不必在运行时初始化的编译时常量。

const std::complex<double> meaning_of_imagination(0, 42); 

Pair that with user defined literals and you have full support for literal user defined classes.

将它与用户定义的文字配对,您就完全支持文字用户定义的类。

3.14D + 42_i;

回答by Kos

There used to be a pattern with metaprogramming:

曾经有一种元编程模式:

template<unsigned T>
struct Fact {
    enum Enum {
        VALUE = Fact<T-1>*T;
    };
};

template<>
struct Fact<1u> {
    enum Enum {
        VALUE = 1;
    };
};

// Fact<10>::VALUE is known be a compile-time constant

I believe constexprwas introduced to let you write such constructs without the need for templates and weird constructs with specialization, SFINAE and stuff - but exactly like you'd write a run-time function, but with the guarantee that the result will be determined in compile-time.

我相信constexpr引入是为了让您无需模板和具有专业化、SFINAE 和其他东西的怪异结构即可编写此类结构 - 但与您编写运行时函数完全一样,但保证结果将在编译中确定-时间。

However, note that:

但是,请注意:

int fact(unsigned n) {
    if (n==1) return 1;
    return fact(n-1)*n;
}

int main() {
    return fact(10);
}

Compile this with g++ -O3and you'll see that fact(10)is indeed evaulated at compile-time!

编译它,g++ -O3你会看到它fact(10)确实在编译时被评估了!

An VLA-aware compiler (so a C compiler in C99 mode or C++ compiler with C99 extensions) may even allow you to do:

一个 VLA-aware 编译器(所以 C99 模式下的 C 编译器或带有 C99 扩展的 C++ 编译器)甚至可以让你做:

int main() {
    int tab[fact(10)];
    int tab2[std::max(20,30)];
}

But that it's non-standard C++ at the moment - constexprlooks like a way to combat this (even without VLA, in the above case). And there's still the problem of the need to have "formal" constant expressions as template arguments.

但是目前它是非标准的 C++ -constexpr看起来是一种解决这个问题的方法(即使没有 VLA,在上述情况下)。仍然存在需要将“正式”常量表达式作为模板参数的问题。

回答by jgibbs

Have just started switching over a project to c++11 and came across a perfectly good situation for constexpr which cleans up alternative methods of performing the same operation. The key point here is that you can only place the function into the array size declaration when it is declared constexpr. There are a number of situations where I can see this being very useful moving forward with the area of code that I am involved in.

刚刚开始将项目切换到 c++11 并遇到了 constexpr 的一个非常好的情况,它清理了执行相同操作的替代方法。这里的关键点是,只有在声明 constexpr 时才能将函数放入数组大小声明中。在许多情况下,我可以看到这对推进我参与的代码区域非常有用。

constexpr size_t GetMaxIPV4StringLength()
{
    return ( sizeof( "255.255.255.255" ) );
}

void SomeIPFunction()
{
    char szIPAddress[ GetMaxIPV4StringLength() ];
    SomeIPGetFunction( szIPAddress );
}

回答by Halcyon

All of the other answers are great, I just want to give a cool example of one thing you can do with constexpr that is amazing. See-Phit (https://github.com/rep-movsd/see-phit/blob/master/seephit.h) is a compile time HTML parser and template engine. This means you can put HTML in and get out a tree that is able to be manipulated. Having the parsing done at compile time can give you a bit of extra performance.

所有其他答案都很棒,我只想举一个很酷的例子,说明你可以用 constexpr 做的一件很棒的事情。See-Phit ( https://github.com/rep-movsd/see-phit/blob/master/seephit.h) 是一个编译时 HTML 解析器和模板引擎。这意味着您可以将 HTML 放入并取出可操作的树。在编译时完成解析可以为您提供一些额外的性能。

From the github page example:

从github页面示例:

#include <iostream>
#include "seephit.h"
using namespace std;



int main()
{
  constexpr auto parser =
    R"*(
    <span >
    <p  color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p  >
    </span>
    )*"_html;

  spt::tree spt_tree(parser);

  spt::template_dict dct;
  dct["name"] = "Mary";
  dct["profession"] = "doctor";
  dct["city"] = "London";

  spt_tree.root.render(cerr, dct);
  cerr << endl;

  dct["city"] = "New York";
  dct["name"] = "John";
  dct["profession"] = "janitor";

  spt_tree.root.render(cerr, dct);
  cerr << endl;
}