您可能知道,C++ 11引入了constexpr关键字.
C++ 11引入了关键字constexpr,它允许用户保证函数或对象构造函数是编译时常量.[...]这允许编译器理解并验证[函数名称]是编译时常量.
我的问题是为什么对可以声明的函数的形式有严格的限制.我理解保证功能是纯粹的愿望,但考虑到这一点:
在函数上使用constexpr会对函数的作用施加一些限制.首先,该函数必须具有非void返回类型.其次,函数体不能声明变量或定义新类型.第三,正文可能只包含声明,空语句和单个return语句.必须存在参数值,以便在参数替换后,return语句中的表达式生成常量表达式.
这意味着这个纯函数是非法的:
constexpr int maybeInCppC1Y(int a, int b)
{
if (a>0)
return a+b;
else
return a-b;
//can be written as return (a>0) ? (a+b):(a-b); but that isnt the point
}
Run Code Online (Sandbox Code Playgroud)
你也不能定义局部变量... :(所以我想知道这是一个设计决定,还是编译器吮吸来证明功能a是纯粹的?
我一直在学习 C++constexpr函数,我实现了一个constexpr递归函数来找到第 n 个斐波那契数。
#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#include <vector>
constexpr long long fibonacci(int num) {
if (num <= 2) return 1;
return fibonacci(num - 1) + fibonacci(num - 2);
}
int main() {
auto start = clock();
long long num = fibonacci(70);
auto duration = (clock() - start) / (CLOCKS_PER_SEC / 1000.);
std::cout << num << "\n" << duration << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
如果我constexpr从fibonacci()函数中删除标识符,则fibonacci(70)需要很长时间来评估(超过 …
以下代码通过指数缓慢的算法计算斐波纳契数:
#include <cstdlib>
#include <iostream>
#define DEBUG(var) { std::cout << #var << ": " << (var) << std::endl; }
constexpr auto fib(const size_t n) -> long long
{
return n < 2 ? 1: fib(n - 1) + fib(n - 2);
}
int main(int argc, char *argv[])
{
const long long fib91 = fib(91);
DEBUG( fib91 );
DEBUG( fib(45) );
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
我在运行时计算第45个Fibonacci数,在编译时计算第91个.
有趣的是,GCC 4.9编译代码并fib91在几分之一秒内完成计算,但需要一段时间才能吐出fib(45).
我的问题:如果GCC足够聪明以优化fib(91)计算而不是采用指数缓慢的路径,那么它会阻止它做同样的事情fib(45)吗?
以上是否意味着GCC产生两个编译版本的fib函数,其中一个是快速的而另一个是指数级的慢? …
我正在编译以下简单程序g++-4.6.1 --std=c++0x:
#include <algorithm>
struct S
{
static constexpr int X = 10;
};
int main()
{
return std::min(S::X, 0);
};
Run Code Online (Sandbox Code Playgroud)
我收到以下链接器错误:
/tmp/ccBj7UBt.o: In function `main':
scratch.cpp:(.text+0x17): undefined reference to `S::X'
collect2: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
我意识到内联定义的静态成员没有定义符号,但我在(可能是有缺陷的)印象中使用constexpr告诉编译器始终将符号视为表达式; 所以,编译器会知道传递对符号的引用是不合法的S::X(出于同样的原因,你不能引用文字10).
但是如果S被声明为命名空间,即"命名空间S"而不是"struct S",那么一切都很好.
这是一个g++错误还是我仍然需要使用技巧来解决这个烦恼?
我正在研究核心常量表达式*中允许的内容,这在C++标准草案的5.19 常量表达式第2段中有所描述:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不考虑[注意:重载的运算符调用函数.-end note]:
并列出随后的子弹中的排除项并包括(强调我的):
- 具有未定义行为的操作 [注意:包括,例如,有符号整数溢出(第5条),某些指针算术(5.7),除零(5.6)或某些移位操作(5.8) - 结束注释];
嗯?为什么常量表达式需要此子句来涵盖未定义的行为?常量表达式是否有一些特殊的东西需要未定义的行为才能在排除中进行特殊划分?
拥有这个条款是否给了我们没有它的任何优势或工具?
作为参考,这看起来像广义常量表达式提案的最新修订版.
我觉得constexpr在C++ 11中的用处有限,因为无法定义两个本来具有相同签名的函数,但有一个是constexpr而另一个不是constexpr.换句话说,如果我有一个constexpr std :: string构造函数只接受constexpr参数,并且非constexpr std :: string构造函数用于非constexpr参数,那将非常有用.另一个例子是理论上复杂的功能,通过使用状态可以提高效率.使用constexpr函数你不能轻易做到这一点,所以你有两个选择:如果你传入非constexpr参数,那么constexpr函数非常慢,或者完全放弃constexpr(或写两个单独的函数,但你可能不知道要调用哪个版本).
因此,我的问题是:
是否有可能符合标准的C++ 11实现允许基于constexpr参数的函数重载,或者这需要更新标准?如果不允许,是否故意不允许?
@NicolBolas:假设我有一个映射enum到a 的函数std::string.最直接的方式做到这一点,假设我enum去从0到n - 1,是创建一个大小的数组n充满了结果.
我可以创建一个static constexpr char const * []并构造一个std::string返回(std::string每次调用函数时支付创建对象的成本),或者我可以创建一个static std::string const []并返回我查找的值,std::string第一次支付所有构造函数的成本调用该函数.似乎更好的解决方案是std::string在编译时创建内存(类似于现在所做的char const *),但是执行此操作的唯一方法是警告构造函数它有constexpr参数.
对于一个除了std::string构造函数之外的例子,我认为找到一个例子是非常简单的,如果你可以忽略constexpr(并因此创建一个非constexpr函数)的要求,你可以创建一个更有效的函数.考虑一下这个帖子:constexpr问题,为什么这两个不同的程序用g ++在不同的时间内运行?
如果我fib用一个constexpr参数调用,我不能比编译器完全优化掉函数调用做得更好.但是,如果我fib使用非constexpr参数调用,我可能希望让它调用我自己的版本来实现memoization(这将需要状态)之类的东西,所以我得到的运行时间类似于我通过constexpr参数时的编译时间.
C++ 11允许使用说明constexpr符声明的函数用于常量表达式,例如模板参数.对于允许的内容有严格的要求constexpr; 本质上这样的函数只包含一个子表达式,而不包含任何其他子表达式.(编辑:这在C++ 14中是放松的,但问题是.)
为什么要求关键字?得到了什么?
它确实有助于揭示接口的意图,但它不会通过保证函数在常量表达式中可用来验证该意图.编写constexpr函数后,程序员必须仍然:
与揭示意图相反,装饰功能constexpr可能会增加错误的安全感,因为在忽略中心语义约束的同时检查了切向句法约束.
简而言之:如果constexpr函数声明仅仅是可选的,那么对语言会有什么不良影响吗?或者对任何有效的程序都会有任何影响吗?
假设我有一个如下定义的整数数组:
static constexpr int IntArray[] = {1, 5, 10, 12, 17};
Run Code Online (Sandbox Code Playgroud)
有没有办法在编译时获得最小值或最大值?
我有一个采用多维的函数,std::vector并需要将深度(或维数)作为模板参数传入。我不想对这个值进行硬编码,我想编写一个constexpr函数,它将std::vector深度作为unsigned integer值返回。
例如:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
Run Code Online (Sandbox Code Playgroud)
这需要在编译时完成,因为这个深度将作为模板参数传递给模板函数:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点?
此constexpr代码未在Visual Studio 2013版本12.0.21005.1 REL中编译
是否有更新的Visual Studio编译器与constexpr一起使用?
#include <iostream>
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
int main(void)
{
const int fact_three = factorial(3);
std::cout << fact_three << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译输出:
1>------ Build started: Project: Project1, Configuration: Debug Win32 ------
1> Source.cpp
1>....\source.cpp(3): error C2144: syntax error : 'int' should be preceded by ';'
1>....\source.cpp(3): error C4430: missing type specifier - int assumed. Note: C++ …