我正在使用整洁的 fmt 库,该库在其版本 8 中会在编译时检查其格式字符串(如果编译器支持相关功能)。
在某些时候,我想编写以下代码:
throw my_exception("error: {}", 123);
Run Code Online (Sandbox Code Playgroud)
可悲的是,天真的实现:
struct my_exception : std::runtime_error {
template<typename... Args>
my_exception(Args&&... args)
: std::runtime_error{fmt::format(std::forward<Args>(args)...)}
{ }
};
Run Code Online (Sandbox Code Playgroud)
失败,因为这会失去字符串文字参数的“consteval-ness”,这是fmt::format
. 目前,我决定以下几点:
template<std::size_t N>
struct literal {
constexpr literal(const char (&str)[N]) noexcept {
std::copy_n(str, N, this->str);
}
char str[N];
};
template<literal lit>
struct exception : std::runtime_error {
template<typename... Args>
exception(Args&&... args)
: std::runtime_error{fmt::format(lit.str, std::forward<Args>(args)...)}
{
}
};
Run Code Online (Sandbox Code Playgroud)
被称为像
throw my_exception<"foo {}">(123);
Run Code Online (Sandbox Code Playgroud)
如何在保持编译时检查的同时恢复正常的函数调用语法?
在 C++20 中,我们有consteval
声明立即函数的关键字。例如:
consteval int f(int x) { return x * x; }
Run Code Online (Sandbox Code Playgroud)
需要这样的函数来生成常量表达式。但是,根据标准,常量表达式不需要在编译时实际求值,除非它用在需要常量的地方,例如在模板参数中。例如,标准中似乎没有任何内容要求在编译时对其进行评估:
int a = f(10);
Run Code Online (Sandbox Code Playgroud)
然而,这些要求强烈表明立即函数应该在编译时进行评估。
标准中似乎也没有要求在编译时评估 constexpr 变量。所以即使我们创建变量constexpr
,即
constexpr int a = f(10);
Run Code Online (Sandbox Code Playgroud)
它只断言这f(10)
是一个常量表达式并生成a
一个常量表达式(但同样,常量表达式不需要在编译时实际评估它们)。然而,就像以前一样,对 constexpr 变量的要求强烈表明它们应该在编译时进行评估。
只有constinit 变量的定义不同 - constinit 变量需要进行静态初始化,因此必须在编译时计算它们并直接嵌入到二进制文件中。然而,这个相关问题的答案说 constexpr 意味着 constinit,至少对于全局变量来说,这似乎与我上面写的内容相矛盾。
那么, consteval 函数和 constexpr 变量是否保证在编译时进行求值?
旁注:我的实际用例涉及尝试使用常量初始化结构中的字段,如下所示:
consteval int my_complicated_calculation() {
// do complicated mathematics and return an int
}
struct A {
int value;
A() : value{my_complicated_calculation()} {}
}
Run Code Online (Sandbox Code Playgroud) 这可能看起来与使用 consteval 而不是 constexpr 函数的优点是什么相同的问题? 但实际上恰恰相反:
现在我们有了 C++20 consteval,在什么(现实)场景中我仍然需要/创建 constexpr 函数而不是 consteval 函数(或普通函数)?
我知道 constexpr 函数也可以使用在运行时评估的参数来调用,这与 consteval 函数不同(请参阅示例代码中的 ***),但为什么我需要它呢?我的意思是我也可以使用普通函数。
int FunNormal()
{
return 12345;
}
consteval int FunConstEval(int p)
{
return p+3;
}
constexpr int FunConstExpr(int p)
{
return p+3;
}
int main()
{
// FunConstEval(FunNormal()); // illegal
FunConstExpr(FunNormal()); // legal, but why would I ever want to do this? ***
// constexpr int a1 = FunNormal(); // illegal, obviously
// constexpr int a1 = FunConstExpr(FunNormal()); // illegal
constexpr …
Run Code Online (Sandbox Code Playgroud) 我试图探究某个功能的含义,inline
并偶然发现了这个问题。考虑这个小程序(demo):
/* ---------- main.cpp ---------- */
void other();
constexpr int get()
{
return 3;
}
int main()
{
std::cout << get() << std::endl;
other();
}
/* ---------- other.cpp ---------- */
constexpr int get()
{
return 4;
}
void other()
{
std::cout << get() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
在不进行优化的情况下进行编译时,该程序将产生以下输出:
3
3
Run Code Online (Sandbox Code Playgroud)
可能不是我们想要的,但是至少我可以解释一下。
constexpr
在编译时计算函数结果,因此决定将其推迟到运行时。constexpr
在功能上意味着 inline
get()
功能碰巧有不同的实现get()
函数声明为静态get()
功能的一种实现碰巧的是,链接器get()
从中选择main.cpp
,返回了3。
现在到我不了解的部分。我只是将get()
功能从更改constexpr
为 …
有什么区别constexpr
和consteval
?
consteval int x1 = 2;
constexpr int x2 = 5;
Run Code Online (Sandbox Code Playgroud)
使用 constexpr 比使用 consteval 更好吗?