大家好,欢迎来到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; }
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