constexpr 真的需要吗?
我一直在研究 C++ 的新 constexpr
功能,但我并不完全理解它的必要性。
例如,以下代码:
constexpr int MaxSize()
{
...
return ...;
}
void foo()
{
int vec[MaxSize()];
}
可以替换为:
int MaxSize()
{
...
return ...;
}
static const int s_maxSize = MaxSize();
foo()
{
int vec[s_maxSize];
}
Update
第二个示例实际上不是标准 ISO C++(感谢几位用户指出了这一点),但某些编译器(例如 gcc)支持它。因此,使程序有效的不是 const,而是 gcc 支持这种非标准功能。 (据我所知,只有当数组被定义为函数或方法的本地数组时,这才是可能的,因为全局数组的大小在编译时仍然必须已知。)如果我在不使用选项 -std= 的情况下进行编译c++98 -pedantic-errors
,甚至代码
int MaxSize()
{
return 10;
}
void foo()
{
int vec[MaxSize()];
}
也会用 gcc 编译。
因此,我将尝试重新表述我的问题,考虑到目前为止收到的反馈(以及我同时完成的一些进一步阅读)。
我大量使用 const
关键字。使用 const,我可以定义一个在其整个生命周期内具有特定值的常量。常量可以使用任何表达式进行初始化,该表达式会计算一次,即在创建常量时。对于这些情况,我认为 constexpr 非常无用:它会引入一个非常小的优化,因为定义常量值的表达式将在编译时而不是运行时计算。每次我需要一个具有复杂初始化的运行时常量时,我都会使用关键字const。
因此,在我们需要在编译时初始化常量的情况下,constexpr 可能会派上用场。一个例子是向量定义:该标准不支持在运行时定义大小。另一个示例是具有一个或多个非类型参数的模板。
在这种情况下,我通常使用宏:
#define MAX_SIZE (10)
void foo()
{
int vec[MAX_SIZE];
}
但是,如果我理解正确的话,constexpr 函数比宏更强大,因为它们允许在其定义中递归调用 constexpr 函数。然而,我想不出任何实际应用程序中我曾经想要使用如此复杂的计算来定义编译时常量。
因此,即使它可能是一个有趣的功能,我仍然想知道是否需要它(即它多久可以解决宏不够的情况)。也许看一些现实生活中无法用宏解决的例子会帮助我改变这个观点。
I have been looking at the new constexpr
feature of C++ and I do not fully understand the need for it.
For example, the following code:
constexpr int MaxSize()
{
...
return ...;
}
void foo()
{
int vec[MaxSize()];
}
can be replaced by:
int MaxSize()
{
...
return ...;
}
static const int s_maxSize = MaxSize();
foo()
{
int vec[s_maxSize];
}
Update
The second example is actually not standard ISO C++ (thanks to several users for pointing this out) but certain compilers (e.g. gcc) support it. So it is not const
that makes the program valid, but the fact that gcc supports this non-standard feature. (To my knowledge, this is possible only when the array is defined as local to a function or method, since the size of a global array must still be known at compile time.) If I compile without the options -std=c++98 -pedantic-errors
, even the code
int MaxSize()
{
return 10;
}
void foo()
{
int vec[MaxSize()];
}
will compile with gcc.
So I will try to rephrase my question taking into account the feedback that came so far (and also some further reading I have done in the mean time).
I use the const
keyword heavily. With const
I can define a constant that has a certain value during its whole lifetime. A constant can be initialized with any expression, which is evaluated once, namely when the constant is created. For these cases, I think that constexpr
is pretty useless: it would introduce a very small optimization in that the expression defining the constant value would be computed at compile time instead of run time. Every time I need a run-time constant with a complex initialization I use the keyword const
.
So constexpr
may come in handy in situations where we need to initialize a constant at compile time. One example is a vector definition: the standard does not support defining the size at runtime. Another example is a template with one or more non-type parameters.
In such cases I normally use macros:
#define MAX_SIZE (10)
void foo()
{
int vec[MAX_SIZE];
}
but, if I understand correctly, constexpr
functions are more powerful than macros, since they allow recursive calls of constexpr
functions in their definition. However, I cannot think of any practical application in which I ever wanted to use such a complex computation to define a compile-time constant.
So, even if it may be an interesting feature, I still wonder if it is needed (i.e. how often it can solve situations where macros are not enough). Maybe looking at a few real-life examples that cannot be solved with macros would help me to change this opinion.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
int vec[s_maxSize];
在第二个示例中实际上是非法的,因此在 C++ 中不可能做到这一点。但你的第一个例子是完全合法的 C++0x。这就是你的答案。实际上你不能按照你在 C++ 中建议的那样去做。它只能在 C++0x 中使用
constexpr
完成。我还想指出,这段代码也适用于 C++0x。在 C++ 中执行此操作需要一些非常奇特的类模板。
当然,在C++0x中你仍然必须使用三元运算符而不是if语句。但是,它可以工作,并且仍然比您在 C++ 中强制使用的模板版本更容易理解:
然后,当然,在 C++ 中您仍然需要编写 gcd如果您需要在运行时计算事物,则可以使用 函数,因为模板不能与运行时变化的参数一起使用。 C++0x 会获得额外的优化提升,因为它知道函数的结果完全由传入的参数决定,这一事实只能用 C++ 中的编译器扩展来表达。
int vec[s_maxSize];
is actually illegal in the second example, so that is not possible to do in C++. But your first example is perfectly legal C++0x.So there's your answer. You can't actually do what you propose in C++. It can only be done in C++0x with
constexpr
.I would also like to point out, that this code also works in C++0x. Doing this in C++ would require some really fancy class templates.
Of course, in C++0x you still have to use the ternary operator instead of if statements. But, it works and is still a lot easier to understand than the template version you'd be force to use in C++:
And then, of course, in C++ you'd still have to write the
gcd
function if you needed to compute things at runtime because the template can't be used with arguments that vary at runtime. And C++0x would have the additional optimization boost of knowing that the result of the function is completely determined by the passed in arguments, which is a fact that can only be expressed with compiler extensions in C++.您可以使用 constexpr 执行但无法使用宏或模板执行的操作,即在编译时解析/处理字符串: 使用 constexpr 进行编译时字符串处理(更改大小写、排序等)。作为前面链接的一小段摘录,constexpr 允许编写如下代码:
作为一个有用的示例,它可以促进某些解析器和正则表达式有限状态机的编译时计算/构造用文字字符串指定。可以推迟到编译时的处理越多,运行时执行的处理就越少。
Something you can do with constexpr that you can not do with macros or templates is parsing /processing strings at compile time: Compile time string processing (changing case, sorting etc.) with constexpr. As a small excerpt from the preceding link, constexpr allows one to write code such as:
As an example of when this could be useful, it could facilitate the compile time computation/construction of certain parsers and regular expression finite-state-machines that are specified with literal strings. And the more processing you can push off to compile time, the less processing you do at run time.
不,不能。那不是合法的 C++03。您有一个可以分配可变长度数组的编译器扩展。
No, it can't. That's not legal C++03. You have a compiler extension that can allocate variable-length arrays.
constexpr 的另一个巧妙技巧允许 if 检测 编译时未定义的行为,这看起来是一个非常有用的工具。以下示例取自我链接的问题,使用 SFINAE 来检测添加是否会导致溢出:
这会导致 (现场观看):
Another neat trick that constexpr allows if to detect undefined behavior at compile time which looks like a very useful tool. The following example taken from the question I linked uses SFINAE to detect if an addition would cause overflow:
which results in (see it live):
constexpr
允许以下内容工作:template
参数确实需要是常量表达式。您的示例有效的唯一原因是可变长度数组 (VLA) - 标准 C++ 中不存在的功能,但可能作为扩展出现在许多编译器中。一个更有趣的问题可能是:为什么不在每个(const)函数上放置
constexpr
?有没有什么害处啊!constexpr
allows the following to work:template
arguments really do need to be constant-expressions. The only reason your example works is because of Variable Length Arrays (VLAs) - a feature that is not in standard C++, but might be in many compilers as an extension.A more interesting question might be: Why not put
constexpr
on every (const) function? Does it do any harm!?根据这个推理,您通常不需要常量,甚至不需要
#define
。没有内联函数或任何东西。与许多关键字一样,
constexpr
的要点是让您更好地表达您的意图,以便编译器准确理解您想要的,而不仅仅是你告诉它的内容,这样它就可以在幕后为你做更好的优化。在此示例中,它允许您编写可维护的函数来计算向量大小,而不仅仅是一遍又一遍地复制和粘贴的纯文本。
By that reasoning you don't need constants in general, not even
#define
. No inline functions or anything.The point of
constexpr
, like so many keywords, is to let you express your intent better so the compiler understands exactly what you want instead of just what you're telling it, so it can do better optimizations for you behind the scenes.In this example, it lets you write maintainable functions to calculate vector sizes instead of just plain text that you copy and paste over and over.