在C++ 11和C++ 14中,为什么我需要constexpr在以下代码段中:
class Foo {
static constexpr double X = 0.75;
};
Run Code Online (Sandbox Code Playgroud)
而这个产生编译错误:
class Foo {
static const double X = 0.75;
};
Run Code Online (Sandbox Code Playgroud)
(更令人惊讶的是)这个编译没有错误?
class Foo {
static const double X;
};
const double Foo::X = 0.75;
Run Code Online (Sandbox Code Playgroud) 这是本主题的某种后续行动,涉及其中的一小部分.与前一个主题一样,让我们考虑一下我们的编译器有和的constexpr函数.现在,让我们直截了当地说.std::initializer_liststd::array
这有效:
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr int a0 = a[0];
constexpr int a1 = a[1];
constexpr int a2 = a[2];
constexpr std::initializer_list<int> b = { a0, a1, a2 };
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这不是:
#include <array>
#include <initializer_list>
int main()
{
constexpr std::array<int, 3> a = {{ 1, 2, 3 }};
constexpr std::initializer_list<int> b = { a[0], a[1], a[2] …Run Code Online (Sandbox Code Playgroud) 我在另一个StackOverflow问题开始(在评论中)的讨论后开始这个问题,我很想知道答案.考虑以下表达式:
var objects = RequestObjects.Where(r => r.RequestDate > ListOfDates.Max());
Run Code Online (Sandbox Code Playgroud)
ListOfDates.Max()在这种情况下,是否有任何(性能)优势可以移出Where子句的评估,还是1.编译器或2. JIT优化它?
我相信C#只会在编译时进行常量折叠,并且可以认为ListOfDates.Max()在编译时是不可知的,除非ListOfDates本身在某种程度上是常量.
也许还有另一个编译器(或JIT)优化,确保只评估一次?
表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:
- new 表达式 ([expr.new]),除非所选分配函数是可替换的全局分配函数 ([new.delete.single]、[new.delete.array]),并且分配的存储空间在E;
放置新表达式不是常量表达式。
为了解决这个问题,C++20 添加了std::construct_at. 那么为什么不能将placement-new表达式设为常量表达式呢?
如果我们看看草案C++标准部分5.1.2 Lambda表达式第2段说(强调我的未来):
lambda表达式的评估导致prvalue临时(12.2).这个临时对象称为闭包对象.lambda表达式不应出现在未评估的操作数中(第5条).[注意:闭包对象的行为类似于函数对象(20.8).-结束注释]
和部分5.19 常量表达式第2段说:
条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2),但是未评估的逻辑AND(5.14),逻辑OR(5.15)和条件(5.16)操作的子表达式不被视为 [...]
并有以下子弹:
- lambda表达式(5.1.2);
那么为什么lambda表达式不允许在未评估的操作数中,但是在常量表达式的未评估部分中是允许的?
我可以看到,对于未评估的操作数,在几种情况下(decltype或typeid)的类型信息不是很有用,因为每个lambda都有一个唯一的类型.虽然为什么我们想要让它们在未经评估的不断表达的背景下不明确,或许是为了让SFINAE?
我想创建一个constexpr函数来返回系统的字节序,如下所示:
constexpr bool IsBigEndian()
{
constexpr int32_t one = 1;
return (reinterpret_cast<const int8_t&>(one) == 0);
}
Run Code Online (Sandbox Code Playgroud)
现在,由于函数将在编译时而不是在实际的目标机器上执行,C++规范给出了什么保证以确保返回正确的结果?
我有以下C代码:
#include <stdint.h>
#include <stdio.h>
int i;
uint64_t a[] = { (uint64_t)&i, (uint64_t)&i + 0x8000000000000000 };
int main() {
printf("%p %llx %llx\n", &i, a[0], a[1]);
}
Run Code Online (Sandbox Code Playgroud)
如果我使用Microsoft Visual Studio Community 2015编译它(作为C或C++)然后运行它,输出类似于以下内容:
013E9154 13e9154 13e9154
Run Code Online (Sandbox Code Playgroud)
看起来,+ 0x8000000000000000我希望设置高位的代码a[1]已经被默默地忽略了.
但是,如果我移动a内部的初始化main,输出就是我所期望的:
00179154 179154 8000000000179154
Run Code Online (Sandbox Code Playgroud)
有了a全球性,为什么要默默地忽略这个添加?尝试添加是否应该实际设置高位a[1]或是否应该导致编译器错误?
有趣的是,如果+ 0x8000000000000000上面的代码被替换为| 0x8000000000000000,我得到"错误C2099:初始化程序不是常量".
编辑:即使没有强制转换,也会出现类似的问题.编译为x64,以下代码打印相同的值(例如000000013FB8D180)三次:
#include <stdio.h>
int i;
int * a[] = { &i, &i + 0x100000000 };
int main() …Run Code Online (Sandbox Code Playgroud) c compiler-errors visual-c++ language-lawyer constant-expression
该constexpr关键字是在C++ 11中引入的,因为(我认为)是"常量表达式"的相应概念.但是,这个概念隐含在C++ 98/c ++ 03中,因为数组声明需要一个常量表达式:
// valid:
int a[sizeof(int)];
int b[3+7];
int c[13/4];
const int n = 3;
int d[n];
// invalid:
int m = 4;
int e[m];
Run Code Online (Sandbox Code Playgroud)
还有其他"常量表达式",即可以在编译时评估(和/或必须)的表达式; 一个例子是模板参数.
对于预C++ 11,在C++ 98/03标准或其他地方是否存在以下情况?
对于以下程序:
struct S { int i; };
constexpr S f() {
return std::is_constant_evaluated() ? S{1} : S{0};
}
int main() {
S s = f();
return s.i;
}
Run Code Online (Sandbox Code Playgroud)
gcc 返回 0,clang 返回 1。 demo
我不认为 的评估f是在需要不断评估的上下文中进行的,所以我认为 clang 在这里是错误的。还是反过来?还是两个结果都有效?
可能重复:
1/0是合法的Java表达式吗?
为什么这段代码会编译?
class Compiles {
public final static int A = 7/0;
public final static int B = 10*3;
public static void main(String[] args) {}
}
Run Code Online (Sandbox Code Playgroud)
如果我查看已编译的类文件,我可以看到B已经被评估为30,而A仍然是7/0.
据我所知,JSL是一个除以零的表达式,它不是常数.
参考:JLS 15.28
我的上述陈述是由于这一行:
编译时常量表达式是表示基本类型值的表达式
因此,除以零不会被评估为原始值.
我真的不明白为什么编译器允许这样做呢?为了清楚起见,上面的代码使用"java.lang.ExceptionInInitializerError"崩溃了运行时
在我看来,编译器威胁任何最终的静态变量作为常量并评估它的编译时间.这意味着编译器已经尝试评估A,但由于它是零除以它只是让它通过.没有编译时错误.但这看起来非常奇怪...编译器知道它是零除以它会崩溃运行时但是它不会标记编译错误!
任何人都可以向我解释原因吗?
java constants compile-time-constant jls constant-expression
c++ ×7
c++11 ×3
c++14 ×2
c++20 ×2
lambda ×2
c ×1
c# ×1
c++03 ×1
c++98 ×1
compile-time ×1
constants ×1
constexpr ×1
endianness ×1
java ×1
jit ×1
jls ×1
optimization ×1
visual-c++ ×1