C++23 将引入if consteval
. 这将在哪里使用,它与constexpr if
?
在 C++20 中添加了一个新功能来获取源位置信息:https : //en.cppreference.com/w/cpp/utility/source_location
这是该页面的一个稍微修改的示例,其中使用附加立即函数loc
来获取源位置:
#include <iostream>
#include <string_view>
#include <source_location>
consteval auto loc(std::source_location x = std::source_location::current() ) { return x; }
void log(const std::string_view message,
const std::source_location location = loc()) {
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << '\n';
}
template <typename T> void fun(T x) { log(x); }
int main(int, char*[]) {
log("Hello world!");
fun("Hello C++20!"); …
Run Code Online (Sandbox Code Playgroud) struct A {
int i;
consteval A() { i = 2; };
consteval void f() { i = 3; }
};
constexpr bool g() {
A a;
a.f();
return true;
}
int main() {
static_assert(g());
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/hafcab7Ga
该计划被 GCC、Clang、MSVC 和 ICC 全部拒绝,取而代之的是constexpr
所有g
四个consteval
机构都接受它。
然而,当删除 call 时a.f();
,仍然带有constexpr
on g
,只有 ICC 仍然拒绝该代码。其他三人现在也都接受了。
我不明白为什么会这样。我的理解是,如果没有consteval
on g
,表达式a.f()
不在直接函数上下文中,这将导致成员函数调用本身被评估为单独的常量表达式,然后无法修改成员,i
因为成员的生命周期在评估期间没有开始那个常数表达式。
但为什么构造函数可以在相同的上下文中对同一个对象执行相同的操作呢?a
的生命周期是否被认为是在 consteval 构造函数求值期间开始的?
另请注意, 的存在static_assert
不会影响这些结果。constexpr …
如果我没记错的话,用户定义的文字的参数在编译时总是已知的。在 C++20 中,您可以使用强制函数在编译时执行,consteval
从而throw
生成编译时错误。
#include <limits>\n\nconsteval int operator""_int(unsigned long long int v) {\n if (std::numeric_limits<int>::max() < v) {\n throw "out of range";\n }\n return static_cast<int>(v);\n}\n\nint main() {\n return 1\'000\'000\'000\'000_int;\n}\n
Run Code Online (Sandbox Code Playgroud)\n$ g++ -std=c++20 main.cpp\nmain.cpp: In function \xe2\x80\x98int main()\xe2\x80\x99:\nmain.cpp:11:12: in \xe2\x80\x98constexpr\xe2\x80\x99 expansion of \xe2\x80\x98operator""_int(1000000000000)\xe2\x80\x99\nmain.cpp:5:9: error: expression \xe2\x80\x98<throw-expression>\xe2\x80\x99 is not a constant expression\n 5 | throw "out of range";\n | ^~~~~~~~~~~~~~~~~~~~\n
Run Code Online (Sandbox Code Playgroud)\n根据我的经验,编译时错误通常比运行时错误更可取。
\n如果必须在定义中调用其他不是 的函数constexpr
,那么consteval
显然不是一个选项。但除了这个例子,我想不出有什么理由不使用consteval
.
还有其他原因不将用户定义的文字标记为 吗consteval …
c++ constant-expression user-defined-literals c++20 consteval
我有一个计算字符串文字的哈希值的函数:
inline consteval uint64_t HashLiteral(const char* key)
{
// body not important here...
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在另一个函数中,我需要文字字符串及其哈希值,我想在编译时计算它:
void function(const char* s)
{
worker(s, HashLiteral(s));
}
Run Code Online (Sandbox Code Playgroud)
然而,似乎不可能进行这样的调用function("string")
,并在编译时在其主体中计算哈希值。我现在想到的最好方法是使用宏,并重新定义函数:
#define MakeHashParms(s) s,HashLiteral(s)
void function(const char* s, const uint64_t hash)
{
worker(s, hash);
}
function(MakeHashParms("string"));
Run Code Online (Sandbox Code Playgroud)
是否可以有更直接的解决方案?
我尝试从我的代码库中提取一个最小的工作示例:
#include <concepts>
enum class unit_type
{
};
template <typename Candidate>
concept unit_c = requires()
{
requires std::semiregular<Candidate>;
{ Candidate::type } -> std::same_as<const unit_type&>;
};
struct unit
{
static constexpr unit_type type{};
};
template<unit_c auto unit_1, unit_c auto unit_2>
struct unit_product
{
static constexpr unit_type type{};
};
template <unit_c Unit1, unit_c Unit2>
consteval unit_c auto operator*(Unit1 unit_1, Unit2 unit_2) noexcept
{
return unit_product<unit_1, unit_2>{};
}
int main()
{
constexpr unit_c auto a = unit{} * unit{};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码编译得很好。但为什么?我认为不能将 …
我有以下功能:
template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
const std::array<SomeEnum, TSize> &arr) {
for (size_t i = 0; i < TSize; ++i) {
if (arr[i] == someEnum) {
return i;
}
}
// How to fail here?
return SOME_DEFAULT_WRONG_VALUE;
}
Run Code Online (Sandbox Code Playgroud)
该函数应该失败而不是返回默认值,但我不能抛出异常或调用assert
. 我可以static_assert
在每次调用函数时添加一个(使用宏它会不那么可怕),但我更喜欢在函数中工作的解决方案。在这种情况下有没有办法触发编译失败?
我很好奇就编译时评估而言,我可以将 gcc 推多远,所以我让它计算了Ackermann函数,特别是输入值为 4 和 1(高于此值的任何值都是不切实际的):
consteval unsigned int A(unsigned int x, unsigned int y)
{
if(x == 0)
return y+1;
else if(y == 0)
return A(x-1, 1);
else
return A(x-1, A(x, y-1));
}
unsigned int result = A(4, 1);
Run Code Online (Sandbox Code Playgroud)
(我认为递归深度限制在~16K,但为了安全起见,我用它编译了这个-std=c++20 -fconstexpr-depth=100000 -fconstexpr-ops-limit=12800000000
)
毫不奇怪,这占用了大量的堆栈空间(实际上,如果以 8mb 的默认进程堆栈大小运行,它会导致编译器崩溃)并且需要几分钟的时间来计算。但是,它最终确实到达了那里,因此显然编译器可以处理它。
在那之后,我决定尝试使用模板、元函数和偏特化模式匹配来实现 Ackermann 函数。令人惊讶的是,以下实现只需几秒钟即可评估:
template<unsigned int x, unsigned int y>
struct A {
static constexpr unsigned int value = A<x-1, A<x, y-1>::value>::value;
};
template<unsigned int y>
struct A<0, y> {
static …
Run Code Online (Sandbox Code Playgroud) 假设我们有一个consteval
函数或一个带有consteval
构造函数的简单结构,它只接受一些值:
struct A
{
consteval A(int a)
{
// compile error if a < 0
if (a < 0)
throw "error";
}
};
Run Code Online (Sandbox Code Playgroud)
有什么方法可以检测这样的构造函数是否可以接受 int 的非类型模板参数?我尝试过以下代码但失败了。
template <int a> concept accepted_by_A = requires() {A(a);};
int main()
{
std::cout << accepted_by_A<-1> << std::endl; // output 1 (true)
}
Run Code Online (Sandbox Code Playgroud) 我知道需求的差异,我最感兴趣的是它带来的代码质量带来的好处。
我能想到的几件事:
consteval
fns 在运行时从不使用(这是推测性的,我没有这方面的真实数据)注意:如果代码质量太模糊,我理解有些人可能想要结束这个问题,对我来说代码质量并不是那么模糊的术语,但是......
例如,其中constexpr
故障被延迟到运行时:
constexpr int div_cx(int a, int b)
{
assert(b!=0);
return a/b;
}
int main()
{
static constexpr int result = div_cx(5,0); // compile time error, div by 0
std::cout << result;
std::cout << div_cx(5,0) ; // runtime error :(
}
Run Code Online (Sandbox Code Playgroud)