今天我想测试一下,Clang 如何转换两个函数的递归幂,并注意到即使使用已知指数,即使使用 constexpr 也不会优化递归。
#include <array>
constexpr unsigned int pow2_recursive(unsigned int exp) {
if(exp == 0) return 1;
return 2 * pow2_recursive(exp-1);
}
unsigned int pow2_5() {
return pow2_recursive(5);
}
Run Code Online (Sandbox Code Playgroud)
pow2_5 被编译为对 pow2_recursive 的调用。
pow2_5(): # @pow2_5()
mov edi, 5
jmp pow2_recursive(unsigned int) # TAILCALL
Run Code Online (Sandbox Code Playgroud)
但是,当我在需要在编译时知道结果的上下文中使用结果时,它将在编译时正确计算结果。
unsigned int pow2_5_arr() {
std::array<int, pow2_recursive(5)> a;
return a.size();
}
Run Code Online (Sandbox Code Playgroud)
被编译为
pow2_5_arr(): # @pow2_5_arr()
mov eax, 32
ret
Run Code Online (Sandbox Code Playgroud)
以下是 Godbolt 中完整示例的链接:https ://godbolt.org/z/fcKef1
那么,我在这里错过了什么吗?有什么可以在运行时改变结果的原因吗,pow2_5 不能像 pow2_5_arr 一样优化?
出于说明目的,我展示了两个小的、略有不同的模板化递归定义。一个使用 an enum,另一个使用static constexpr定义一个值。
我检查了两个程序的输出程序集,它们完全相同,并且在语义上它们看起来也相同。
我认为constexpr可能更现代一些,但是使用enum/之间有什么区别static constexpr,或者是否有任何特定用例的区别真的很重要?
// using enum
template<uint64_t N>
struct Sum {
enum : uint64_t { value = N + Sum<N - 1>::value };
};
template<>
struct Sum<0> {
enum : uint64_t { value = 1 };
};
Run Code Online (Sandbox Code Playgroud)
// using static constexpr
template<uint64_t N>
struct Sum {
static constexpr uint64_t value = N + Sum<N - 1>::value;
};
template<>
struct Sum<0> {
static constexpr uint64_t value = 1; …Run Code Online (Sandbox Code Playgroud) 这个程序:
constexpr void f() { x: ; }
Run Code Online (Sandbox Code Playgroud)
由 gcc 编译,但 clang 说:
error: statement not allowed in constexpr function
Run Code Online (Sandbox Code Playgroud)
那么这个代码有效吗?
我正在实现一个constexpr int foo();功能。在 的主体中foo(),我想在编译时做一些不同的事情(并且可能返回一些不同的东西),而在运行时做一些不同的事情。
使用 C++20,我可以使用std::is_constant_evaluated:
constexpr int foo() { return std::is_constant_evaluated() ? 123 : 456 };
Run Code Online (Sandbox Code Playgroud)
但是如果我使用的是 C++17(或更早版本)怎么办 - 我可以用同样的效果做什么?
注意:特定于编译器的解决方案是可以接受的(尽管不太理想)。
我正在通过 C++ Primer 中的练习进行练习,并找到了练习 2.32 的在线解决方案。
我知道以下代码是非法的,因为初始化无法从 int 转换为 int*:
int null = 0, *p = null;
但是,提到的两个解决方案是我没有提出的:
const int null3 = 0, *p3 = null3;
constexpr int null4 = 0, *p4 = null4;
Run Code Online (Sandbox Code Playgroud)
为什么在编译期间允许这些没有错误?我仍然期待 p3 和 p4 的初始化需要 & 来表示地址 (&null3, &null4)。
这是我的记事本文件中的代码:
#include <iostream>
int main()
{
// int null = 0, *p = null; // it is not legal; depending on intent there are two ways to fix (that I can think off atm)
{
int null …Run Code Online (Sandbox Code Playgroud) 我最近使用constexprC++17 的函数编写了一个编译时光线追踪器。完整的源代码可以在这里看到。此问题的相关代码如下所示:
constexpr auto image = []() {
StaticImage<image_width, image_height> image;
Camera camera{Pointf{0.0f, 0.0f, 500.0f},
Vectorf{0.0f},
Vectorf{0.0f, 1.0f, 0.0f},
500.0f};
std::array<Shapes, 1> shapes_list{Sphere{Pointf{0.0f}, 150.0f}};
std::array<Materials, 1> materials_list{DefaultMaterial{}};
ShapeContainer<decltype(shapes_list)> shapes{std::move(shapes_list)};
MaterialContainer<decltype(materials_list)> materials{
std::move(materials_list)};
SphereScene scene;
scene.set_camera(camera);
Renderer::render(scene, image, shapes, materials);
return image;
}();
Run Code Online (Sandbox Code Playgroud)
其中每个这里示出的类(StaticImage,Camera,Shapes,Materials,ShapeContainer,MaterialContainer,和SphereScene)由完全constexpr的功能。Renderer::render也constexpr负责循环图像中的每个像素,将光线射入场景,并设置相应的颜色。
使用当前的设置和 512x512 的图像,在发布模式下使用 MSVC 16.9.2,编译器需要大约 35 分钟来完成生成图像。在此过程中,其内存使用量上升到最终使用近 64GB RAM 的程度。
所以,我的问题是:为什么编译时间和内存使用率这么高?
我的理论是编译时间的部分原因是调用堆栈的复杂性(即大量模板、CRTP 和深度),所以我尝试通过删除几个模板来稍微简化调用堆栈( …
我有一个基类,打算由我正在编写的代码的其他用户继承,其中一个抽象函数返回对象的名称。由于项目的性质,名称不能包含空格。
class MyBaseClass {
public:
// Return a name for this object. This should not include whitespace.
virtual const char* Name() = 0;
};
Run Code Online (Sandbox Code Playgroud)
有没有办法在编译时检查Name()函数的结果是否包含空格?我知道constexpr函数可以进行编译时操作,但我不确定以正确的方式向代码用户发出信号,告知他们的函数返回一个顽皮的字符串。
我也不清楚如何让constexpr编译器实际执行一个函数来执行这样的检查(如果constexpr是这样的话)。
我正在尝试将递归与可变参数模板一起使用。我希望基本情况具有零模板参数。在查看了以前问题的 stackoverflow 答案后,我发现了对这个问题的两种回应:
template <typename = void> 或template <typename T = void> 。例如,这里的第一个答案:如何编写可变参数模板递归函数?我试图在我的问题中使用解决方案 (2),但收到错误。这是一个最小的、可重现的示例:
#include <iostream>
template<typename = void> // base case
int NumArguments() {
return 0;
}
template<typename FirstArg, typename... RemainingArgs>
int NumArguments() {
return 1 + NumArguments<RemainingArgs...>();
}
Run Code Online (Sandbox Code Playgroud)
class A {
public:
A() {}
};
int main() {
std::cout << NumArguments<A>();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在 Microsoft Visual C++20 中编译给出了错误:
error C2668: 'NumArguments': ambiguous call to overloaded function …Run Code Online (Sandbox Code Playgroud) C++23 将引入if consteval. 这将在哪里使用,它与constexpr if?
我一直以为:
const_casted 变量是 UB所以我很困惑为什么这段代码会编译:
constexpr int fn(){
int v = 42;
return [v]() {
const_cast<int&>(v)+=5;
return v;
}();
}
static constexpr auto val = fn();
int main() {
return val;
}
Run Code Online (Sandbox Code Playgroud)
注意:我知道没有理由不允许这样做,因为结果应该是显而易见的,我对为什么允许这样做的法律解释更感兴趣。