C++20尝鲜:常量表达式变化

C++20尝鲜:常量表达式变化Language FeatureProposalLess eager instantiation of constexpr functionsP

大家好,欢迎来到IT知识分享网。

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; }
C++20尝鲜:常量表达式变化

在线编译测试

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); } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/49590.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信