我一直在使用 Godbolt 编译器并输入以下代码:
constexpr int func(int x)
{
return x > 3 ? x * 2 : (x < -4 ? x - 4 : x / 2);
}
int main(int argc)
{
return func(argc);
}
Run Code Online (Sandbox Code Playgroud)
代码有点简单。这里重要的部分是里面最后除以2 func(int x)。由于 x 是一个整数,基本上任何编译器都会将其简化为移位以避免除法指令。
x86-64 gcc 12.2(对于 Linux,因此 System V ABI)的程序集-O3如下所示:
main:
cmp edi, 3
jle .L2
lea eax, [rdi+rdi]
ret
.L2:
cmp edi, -4
jge .L4
lea eax, [rdi-4]
ret
.L4:
mov eax, edi
mov ecx, 2
cdq
idiv …Run Code Online (Sandbox Code Playgroud) c++ g++ integer-division compiler-optimization constexpr-function
constexpr 函数的标准在[decl.constexpr] 的第 5 点下说明:
对于非模板、非默认的 constexpr 函数或非模板、非默认、非继承的 constexpr 构造函数,如果不存在参数值使得函数或构造函数的调用可以是核心常量的计算子表达式表达式 (5.19),程序格式错误;无需诊断。
它继续为此提供以下示例:
constexpr int f(bool b){ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
Run Code Online (Sandbox Code Playgroud)
我从中得到的是,带有空参数列表的函数是非诊断格式错误的。这让我觉得非常奇怪,以至于我怀疑我的理解是不正确的。例如,这是否也是格式错误的:
constexpr int g() { return 0; } // ill-formed?
Run Code Online (Sandbox Code Playgroud)
如果是这样,这背后的基本原理是什么,如果不是,则限定意味着什么/ constexpr 函数何时变得格式错误?
想必以下这些都可以吧?
constexpr int h(int x) { return x; } // presumably fine?
constexpr int l = h(42); // also fine
Run Code Online (Sandbox Code Playgroud) 我刚刚发现一个constexpr方法可以正确返回在执行过程中改变的类成员的值。我的问题是,如果constexpr应该在编译时完全评估方法,这怎么可能?
下面的示例正确输出Value: 0,然后Value: 5. 更重要的是,如果我a.change(5)将编译器更改为不可预测的内容(例如a.change(atoi(argv[1]))或者a.change(rand() % 10 + 1)它仍然有效。为什么?为什么它甚至编译?
#include <iostream>
class A
{
public:
void change(int value) { x = value; }
int get() const { return x; }
constexpr int value() const noexcept { return x; }
private:
int x{0};
};
int main()
{
A a;
std::cout << "Value: " << a.get() << std::endl;
a.change(5);
std::cout << "Value: " << a.get() << …Run Code Online (Sandbox Code Playgroud) 当我使用 constexpr 函数时,我注意到一个奇怪的行为。我将代码简化为一个简化的示例。从两个不同的翻译单元(模块 A 和 B)调用两个函数。
#include <iostream>
int mod_a();
int mod_b();
int main()
{
std::cout << "mod_a(): " << mod_a() << "\n";
std::cout << "mod_b(): " << mod_b() << "\n";
std::cout << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这些模块看起来很相似。这是mod_a.cpp:
constexpr int X = 3;
constexpr int Y = 4;
#include "common.h"
int mod_a()
{
return get_product();
}
Run Code Online (Sandbox Code Playgroud)
只有一些内部常数不同。这是mod_b.cpp:
constexpr int X = 6;
constexpr int Y = 7;
#include "common.h"
int mod_b()
{
return get_product();
}
Run Code Online (Sandbox Code Playgroud)
constexpr两个模块都使用“common.h”中定义的通用函数:
/* static */ …Run Code Online (Sandbox Code Playgroud) virtual我对我读到的有关混合和constexpr成员函数的内容感到困惑。根据:
直到 C++17 为止,应该不可能混合constexpr和virtual(标准在上面的链接中引用)
然而我设计了这个简单的例子:
#include <cstddef>
struct SizedObject {
virtual size_t GetSize() const = 0;
};
struct DynSizedObject : public SizedObject {
size_t s;
size_t GetSize() const override final { return s; }
};
struct StatSizedObject : public SizedObject {
const size_t s;
constexpr size_t GetSize() const override final { return s; }
constexpr explicit StatSizedObject(const size_t i) : s(i) {}
};
int …Run Code Online (Sandbox Code Playgroud) 假设我们有 const 数组:
const int g_Values[] = { ... };
Run Code Online (Sandbox Code Playgroud)
如何检查成员在编译时单调增长,即g_Values[i] < g_Values[i + 1]
在运行时可以这样检查:
bool IsMonotonously()
{
int i = _countof(g_Values);
int m = MAXINT;
do
{
int v = g_Values[--i];
if (v >= m) return false;
m = v;
} while (i);
return true;
}
Run Code Online (Sandbox Code Playgroud)
但如何用 constexpr 重写它并 if IsMonotonously()return false- 生成编译时错误。
我试图在编译时计算一个数组以加速某些函数,但遇到一个错误,但无法在 cppreference 的帮助下解决该错误。
代码归结为:
#include <cstddef>
#include <array>
template<typename T, size_t size>
constexpr auto giveArray()
{
std::array<T, size> arr;
for(size_t i = 0; i < size; ++i)
arr[i] = 0;
return arr;
}
constexpr auto arr = giveArray<int,10>().data();
Run Code Online (Sandbox Code Playgroud)
在 ubuntu 上使用“$ g++ -std=c++20 code.cpp”进行编译时,出现错误:.data() 不是 constexpr 函数,但它确实是。为什么我会收到此错误以及如何修复它,同时仍在编译时运行此函数并仅存储指针,而不存储 std::array 对象?
注意:我使用的是 gcc,但在 godbolt.org 上进行了测试,它也适用于 msvc,但不适用于 clang
\n我意外地发现以下简单函数在模板类中进行编译,但不能作为自由函数进行编译。有人可以解释为什么吗?
\n编译正常:
\n template <typename T = void>\n class A\n {\n public:\n static constexpr std::string f()\n {\n return std::string();\n }\n }\nRun Code Online (Sandbox Code Playgroud)\n不编译:
\n constexpr std::string f()\n {\n return std::string();\n }\nRun Code Online (Sandbox Code Playgroud)\n抛出错误:
\nerror: invalid return type \xe2\x80\x98std::string\xe2\x80\x99 {aka \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99} of \xe2\x80\x98constexpr\xe2\x80\x99 function ...\n...\n/usr/include/c++/9/bits/basic_string.h:77:11: note: \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 is not literal because:\n 77 | class basic_string\n | ^~~~~~~~~~~~\n/usr/include/c++/9/bits/basic_string.h:77:11: note: \xe2\x80\x98std::__cxx11::basic_string<char>\xe2\x80\x99 has a non-trivial destructor\nRun Code Online (Sandbox Code Playgroud)\n 我可以很容易地说,通过将函数声明为constexpr,我们可以在编译时对其进行评估,这可以节省运行时的时间,因为结果已经生成了。
另一方面,虚函数需要在运行时解析。因此,我想我们无法摆脱解决过程。由于函数的机制,只能快速获取结果constexpr。
函数还有其他好处吗constexpr virtual?
说明
constexpr符仅应用于变量或变量模板的定义或者函数或函数模板的声明。说明consteval符仅适用于函数或函数模板的声明。constexpr使用or说明符声明的函数或静态数据成员consteval隐式是内联函数或变量 ([dcl.inline])。如果函数或函数模板的任何声明具有constexpr或consteval说明符,则其所有声明都应包含相同的说明符。Run Code Online (Sandbox Code Playgroud)constexpr void square(int &x); // OK, declaration constexpr int bufsz = 1024; // OK, definition constexpr struct pixel { // error: pixel is a type int x; int y; constexpr pixel(int); // OK, declaration }; constexpr pixel::pixel(int a) : x(a), y(x) // OK, definition { square(x); } constexpr pixel small(2); // error: square not defined, so small(2) <<<<<<<<<<<<<<<<<<<< // not constant ([expr.const]) so …