网站首页 > 教程分享 正文
Language Feature | Proposal |
Less eager instantiation of constexpr functions | P0859R0 |
Allowing Virtual Function Calls in Constant Expressions | P1064R0 |
Immediate functions (consteval) | P1073R3 |
P1937R2 | |
std::is_constant_evaluated | P0595R2 |
Relaxations of constexpr restrictions | P1002R1 |
P1327R1 | |
P1330R0 | |
P1331R2 | |
P1668R1 | |
P0784R7 |
consteval 立即函数
consteval 说明符声明函数或函数模板为立即函数,即该函数的每次潜在求值的调用必须产生编译时常量表达式。它不可应用于析构函数、分配函数或解分配函数。consteval 说明符蕴含 inline 。同一声明说明符序列中允许出现至多一个 constexpr 、 consteval 及 constinit 说明符。
#include <iostream>
consteval int GetInt(int x){
return x;
}
constexpr int f(){
int x1 = GetInt(1);
//constexpr auto x2 = GetInt(x1); // 错误, x1 不是常量表达式
return x1;
}
int main()
{
auto v = f();
std::cout << "f:" << v << std::endl;
return 0;
}
https://wandbox.org/nojs/gcc-head
https://wandbox.org/nojs/clang-head
允许constexpr 虚函数
虚函数现在可以是constexpr。Constexpr函数可以覆盖非Constexpr函数,反之亦然。
#include <iostream>
struct Base{
virtual int Get() const = 0; // non-constexpr
};
struct Derived1 : Base{
constexpr int Get() const override {
return 1;
}
};
struct Derived2 : Base{
constexpr int Get() const override {
return 2;
}
};
constexpr auto GetSum(){
const Derived1 d1;
const Derived2 d2;
const Base* pb1 = &d1;
const Base* pb2 = &d2;
return pb1->Get() + pb2->Get();
};
int main()
{
static_assert(GetSum()); // 编译时赋值
std::cout << "GetSum:" << GetSum() << std::endl;
return 0;
}
constexpr 中使用 try-catch 块
现在允许在constexpr函数中使用try-catch块,但是throw不可以,因此,catch块被简单地忽略。这可能很有用,例如,与constexpr new结合使用,我们可以有一个在运行/编译时工作的函数。
#include <iostream>
constexpr int f(int x){
try{
if( x > 0){
auto p = new int;
*p = 1;
x += *p;
delete p;
}else{
throw x;
}
}
catch(...){ // 忽略,编译时,运行时保留
// ...
int y = 1;
return x - y;
}
return x;
}
int main()
{
static_assert(f(1) == 2); // 编译时赋值
std::cout << "f:" << f(0) << std::endl; //运行时
return 0;
}
constexpr 中使用 dynamic_cast和多态类型typeid
在constexpr中允许动态类型转换和多态类型typeid 。可惜的是,std::type_info还没有constexpr成员,所以没啥用。
#include <iostream>
#include <typeinfo>
#include <string>
struct Base1{
constexpr virtual int get() const = 0;
};
struct Derived1 : Base1{
constexpr int get() const override {
return 1;
}
};
struct Base2{
constexpr virtual int get() const = 0;
};
struct Derived2 : Base2{
constexpr int get() const override {
return 2;
}
};
template<typename Base, typename Derived>
constexpr auto downcasted_get(){
const Derived d;
const Base& upcasted = d;
const auto& downcasted = dynamic_cast<const Derived&>(upcasted);
return downcasted.get();
}
template<typename Base, typename Derived>
constexpr auto print_type(){
Derived d1;
const Base *pb = &d1;
return typeid(*pb).name(); // name() 不是constexpr
}
int main()
{
static_assert(downcasted_get<Base1, Derived1>() == 1);
static_assert(downcasted_get<Base2, Derived2>() == 2);
//static_assert(downcasted_get<Base2, Derived1>() == 1); //编译时错误,不能将Deriv1强制转换为Base2
//static_assert(print_type<Base1, Derived1>());
return 0;
}
在constexpr内更改联合体的有效成员
#include <string>
union Foo {
int i;
float f;
};
constexpr int f() {
Foo foo{};
foo.i = 3; // i 是有效成员
foo.f = 1.2f; // 从C++20起, f 变成有效成员
// return foo.i; // 错误, 读取无效union成员
return foo.f;
}
int main()
{
static_assert(f() == 1);
return 0;
}
constexpr 中使用allocator
允许调用std::allocator<T>::allocate()和new-expression,内存可以在编译时分配,但也必须在编译时释放。
#include <iostream>
#include <string>
#include <array>
constexpr auto get_str()
{
std::string s1{"hello "};
std::string s2{"world"};
std::string s3 = s1 + s2;
return s3;
}
constexpr auto get_array()
{
constexpr auto N = get_str().size();
std::array<char, N> arr{};
std::copy_n(get_str().data(), N, std::begin(arr));
return arr;
}
int main()
{
static_assert(!get_str().empty());
// 错误,因为其持有编译时内存分配
// constexpr auto str = get_str();
// 正确, 字符串存储在 std::array<char>
constexpr auto arr = get_array();
std::copy(arr.cbegin(), arr.cend(), std::ostream_iterator<char>(std::cout, ""));
return 0;
}
注意:此例子仅支持MSVC STL 19.29,在撰写本文时没有其他编译器。我在godbolt上测试了11.1版本的GCC编译器,简单的constexpr std::string在这个版本中仍然会产生错误。
在constexpr函数中进行简单的默认初始化
在C++17中,除其他要求外,constexpr构造函数必须初始化所有非静态数据成员。该规则在C++20中被删除了。但是,因为在constexpr上下文中不允许使用未定义行为,所以你不能从这些未初始化的成员中读取,只能写入它们。
#include <iostream>
#include <string>
struct NonTrivial{
bool b = false;
};
struct Trivial{
bool b;
};
template <typename T>
constexpr T f1(const T& other) {
T t; // default initialization
t = other;
return t;
}
template <typename T>
constexpr auto f2(const T& other) {
T t;
return t.b;
}
int main(){
constexpr auto a = f1(Trivial{}); // 在C++17中错误,,在 C++20中正确
constexpr auto b = f1(NonTrivial{});// OK
//constexpr auto c = f2(Trivial{}); // 错误, 未初始化Trivial::b被使用
constexpr auto d = f2(NonTrivial{}); // OK
}
constexpr函数中使用未计算的asm声明
asm声明现在可以出现在constexpr函数中,以防它在编译时没有被计算。这样就可以在一个函数中同时有编译和运行时(现在有asm)的代码。
#include <iostream>
#include <string>
constexpr int add(int a, int b){
if (std::is_constant_evaluated()){
return a + b;
}
else{
asm("asm magic here");
return 0;
}
}
int main(){
static_assert(add(1,2) == 1+2);
}
std::is_constant_evaluated()
通过std::is_constant_evaluated(),您可以检查当前调用是否发生在常量求值的上下文中。
#include <iostream>
#include <assert.h>
constexpr int GetNumber(){
if(std::is_constant_evaluated()){ // should not be `if constexpr`
return 1;
}
return 2;
}
constexpr int GetNumber(int x){
if(std::is_constant_evaluated()){ // should not be `if constexpr`
return x;
}
return x+1;
}
int main(){
constexpr auto v1 = GetNumber();
const auto v2 = GetNumber();
// initialization of a non-const variable, not constant-evaluated
auto v3 = GetNumber();
assert(v1 == 1);
assert(v2 == 1);
assert(v3 == 2);
constexpr auto v4 = GetNumber(1);
int x = 1;
// x is not a constant-expression, not constant-evaluated
const auto v5 = GetNumber(x);
assert(v4 == 1);
assert(v5 == 2);
}
猜你喜欢
- 2024-10-18 信号 - Linux Signal - 网络编程的相关信号
- 2024-10-18 干货 | 一文搞定 pytest 自动化测试框架(一)
- 2024-10-18 linux网络编程—tcp和udp基本函数调用过程及如何选择
- 2024-10-18 C语言之结构体基础(c语言结构体经典例题)
- 2024-10-18 linux定时器编程详解(包含代码)(linux定时器执行脚本)
- 2024-10-18 C语言学习第16篇---三目运算符和逗号表达式
- 2024-10-18 Linux网络编程相关高级I/O函数 - 用于创建文件描述符的函数
- 2024-10-18 干货 | 一文搞定 pytest 自动化测试框架(二)
- 2024-10-18 现代c++之移动构造,移动赋值,拷贝构造,拷贝赋值
- 2024-10-18 探索C语言断言:保证程序的健壮性和可靠性
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- css导航条 (66)
- sqlinsert (63)
- js提交表单 (60)
- param (62)
- parentelement (65)
- jquery分享 (62)
- check约束 (64)
- curl_init (68)
- sql if语句 (69)
- import (66)
- chmod文件夹 (71)
- clearinterval (71)
- pythonrange (62)
- 数组长度 (61)
- javafx (59)
- 全局消息钩子 (64)
- sort排序 (62)
- jdbc (69)
- php网页源码 (59)
- assert h (69)
- httpclientjar (60)
- postgresql conf (59)
- winform开发 (59)
- mysql数字类型 (71)
- drawimage (61)
本文暂时没有评论,来添加一个吧(●'◡'●)