我一直在研究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];
}
更新
第二个例子实际上不是标准的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
函数。但是,我想不出任何实际的应用程序,我曾想用这种复杂的计算来定义编译时常量。
所以,即使它可能是一个有趣的功能,我仍然想知道是否需要它(即它可以解决宏不足的情况)。也许看一些用宏无法解决的现实例子可以帮助我改变这种观点。
答案 0 :(得分:18)
int vec[s_maxSize];
在第二个例子中实际上是非法的,因此在C ++中是不可能的。但是你的第一个例子是完全合法的C ++ 0x。
所以这是你的答案。你实际上无法做你在C ++中提出的建议。它只能在带有constexpr
的C ++ 0x中完成。
我还想指出,这段代码也适用于C ++ 0x。在C ++中执行此操作需要一些非常精美的类模板。
constexpr unsigned int gcd(unsigned int const a, unsigned int const b)
{
return (a < b) ? gcd(b, a) : ((a % b == 0) ? b : gcd(b, a % b));
}
char vec[gcd(30, 162)];
当然,在C ++ 0x中,您仍然必须使用三元运算符而不是if语句。但是,它比你在C ++中强制使用的模板版本更有效,并且更容易理解:
template <unsigned int a, unsigned int b>
class gcdT {
public:
static unsigned int const value = gcdT<b, a % b>::value;
};
template <unsigned int a>
class gcdT<a, 0> {
public:
static unsigned int const value = a;
};
char vec[gcdT<30, 162>::value];
然后,当然,在C ++中,如果需要在运行时计算内容,则仍需要编写gcd
函数,因为模板不能与运行时变化的参数一起使用。知道函数的结果完全由传入的参数决定,C ++ 0x会有额外的优化提升,这是一个事实,只能用C ++中的编译器扩展来表达。
答案 1 :(得分:7)
你可以用constexpr做的事情是你无法用宏或模板做的事情是在编译时解析/处理字符串:Compile time string processing (changing case, sorting etc.) with constexpr。作为上述链接的一小段摘录,constexpr允许用户编写如下代码:
#include "my_constexpr_string.h"
int main()
{
using namespace hel;
#define SDUMP(...) static_assert(__VA_ARGS__, "")
SDUMP(tail("abc") == "bc");
SDUMP( append("abc", "efgh") == "abcefgh" );
SDUMP( prepend("abc", "efgh") == "efghabc" );
SDUMP( extract<1,3>("help") == "el" );
SDUMP( insert<1>("jim", "abc") == "jabcim" );
SDUMP( remove("zabzbcdzaz", 'z') == "abbcdazzzz" );
SDUMP( erase("z12z34z5z", 'z') == "12345" );
SDUMP( map("abc", ToUpper()) == "ABC" );
SDUMP( find("0123456777a", '7') == 7 );
SDUMP( isort("03217645") == "01234567");
}
作为一个有用的例子,它可以促进编译时计算/构造某些解析器和使用文字字符串指定的正则表达式有限状态机。而且你可以推动编译时间越多,你在运行时的处理就越少。
答案 2 :(得分:3)
int MaxSize() {
...
return ...; }
static const int s_maxSize = MaxSize();
int vec[s_maxSize];
不,它不能。这不是合法的C ++ 03。您有一个可以分配可变长度数组的编译器扩展。
答案 3 :(得分:0)
constexpr
允许以下工作:
#include<iostream>
using namespace std;
constexpr int n_constexpr() { return 3; }
int n_NOTconstexpr() { return 3; }
template<size_t n>
struct Array { typedef int type[n]; };
typedef Array<n_constexpr()>::type vec_t1;
typedef Array<n_NOTconstexpr()>::type vec_t2; // fails because it's not a constant-expression
static const int s_maxSize = n_NOTconstexpr();
typedef Array<s_maxSize>::type vec_t3; // fails because it's not a constant-expression
template
参数确实需要是常量表达式。您的示例工作的唯一原因是因为可变长度数组(VLA) - 一种不在标准C ++中的功能,但可能在许多编译器中作为扩展。
一个更有趣的问题可能是:为什么不在每个(const)函数上放置constexpr
?它有害吗??
答案 4 :(得分:0)
另一个巧妙的技巧 constexpr 允许在编译时检测undefined behavior,这看起来是一个非常有用的工具。从我链接的问题中获取的以下示例使用 SFINAE 来检测添加是否会导致溢出:
#include <iostream>
#include <limits>
template <typename T1, typename T2>
struct addIsDefined
{
template <T1 t1, T2 t2>
static constexpr bool isDefined()
{
return isDefinedHelper<t1,t2>(0) ;
}
template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
static constexpr bool isDefinedHelper(int)
{
return true ;
}
template <T1 t1, T2 t2>
static constexpr bool isDefinedHelper(...)
{
return false ;
}
};
int main()
{
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
std::cout << std::boolalpha <<
addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}
导致( see it live ):
true
false
true
答案 5 :(得分:-4)
根据这个推理,你通常不需要常量,甚至不需要#define
。没有内联函数或任何东西。
与许多关键字一样,constexpr
的目的是让您更好地表达您的意图,以便编译器准确理解您想要的而不仅仅是你告诉它什么,所以它可以在幕后为你做更好的优化。
在此示例中,它允许您编写可维护的函数来计算矢量大小,而不仅仅是您反复复制和粘贴的纯文本。