C++23新特性个人总结

2023-11-17

文章目录


C++23新特性

推荐编译器版本:GCC 13

__cplusplus:待定

编译选项:-std=c++23或-std=gnu++2b

1 关键字

1.1 consteval

编译器版本:GCC 12

文档链接P1938R3: if consteval

新增版本:C++20,可查看C++20新特性进行回顾

扩展适用范围,新增支持if表达式

前版本存在的问题:

#include <type_traits>

// 立即函数
consteval int f(int i) 
{ 
    return i; 
}

constexpr int g(int i) 
{
    // 标准库中,is_constant_evaluated函数声明为constexpr
    if constexpr (std::is_constant_evaluated()) 
    {
        // 当前为常量语境时执行
        return f(i) + 1; // error,g()函数并非常量表达式
    } 
    else 
    {
        return 42;
    }
}

// 立即函数
consteval int h(int i) 
{
    return f(i) + 1;
}

在语义角度看,既然if constexpr (std::is_constant_evaluated())已经限定了常量语境了,为何还不能调用立即函数?但是constexpr和consteval的底层机理并不一致,所以并不相通。

因此为解决这个问题,新增 if consteval { } 来代替 if(std::is_constant_evaluated())。

语法:if consteval {} consteval前后不能有圆括号,后面必须接花括号

新版例子:

#include <type_traits>

// 立即函数
consteval int f(int i) 
{ 
    return i; 
}

constexpr int g(int i) 
{
    // if consteval后面的花括号不可省略
    if consteval // 否定形式:not consteval 或 ! consteval
    {
        return f(i) + 1;
    } 
    else 
    {
        return 42;
    }
}

1.2 auto

1.2.1 新增支持数组指针的引用类型

编译器版本:GCC 12

文档链接CWG 2397: auto specifier for pointers and references to arrays

例子:

int main()
{
    int a[3];
    auto (*p)[3] = &a;
    return 0;
}

1.2.2 代替decay-copy语义

编译器版本:GCC 12

文档链接P0849R8: auto(x): decay-copy in the language

不太严谨地说,decay-copy语义其实是将一个变量复制一份生成其对应的prvalue。新特性下,auto将可以表示decay-type,auto(x)代替decay-copy语义。

例子:

#include <iostream>

struct A 
{
    A() {}

    // 拷贝构造函数
    A(const A&) {}
    // 移动构造函数
    A(A&&) {}
};

void f(A&) 
{
    std::cout << "A(A&)" << std::endl;
}
void f(A&&)
{
    std::cout << "A(A&&)" << std::endl;
}

void h() 
{
    A a;
    std::cout << "===11====" << std::endl;
    // 输出A(A&),因为a是左值
    f(a);  
    std::cout << "===22====" << std::endl;
    // 输出A(A&&),因为a是左值,所以先调用A的拷贝构造函数创建prvalue
    f(A(a)); 
    std::cout << "===33====" << std::endl;
    // 输出A(A&&),因为a是左值,所以先调用A的拷贝构造函数创建prvalue,此处auto表示A类型
    f(auto(a)); 
    std::cout << "===44====" << std::endl;
    // 输出A(A&&),因为a是左值,所以先调用A的拷贝构造函数创建prvalue
    // 里层的auto表示A类型,返回一个prvalue;而因为里层已经prvalue,外层的auto则是什么都不干
    f(auto(auto(a))); // 
    std::cout << "===55====" << std::endl;
    // 等号左边的auto表示A*类型,等号右边的auto表示A类型
    auto t = new auto(a);
    // 此处的auto表示A类型
    f(auto(*t)); // 编译成功
    f(auto(t)); // 编译错误,因为auto表示A类型,但是t是A*类型
    delete t;
}

1.3 volatile

编译器版本:GCC 13

文档链接P2327R1: De-deprecating volatile compound operations

还原C++20中volatile弃用的特性

1.4 constexpr

编译器版本:GCC 13

文档链接P2448R2: Relaxing some constexpr restrictions

新增版本:C++11

放宽constexpr限制,constexpr函数内可以使用控制流语句和变量初始化操作以及运行时的条件(如if、switch、for、while等),以便更好适应实际应用场景。

例子:

#include <iostream>

constexpr void func(int i) 
{
    int a = i * i + i;
    if(i == 1)
    {
        std::cout << "111111" << std::endl;
    }
    else 
    {
        std::cout << "222222" << std::endl;
    }

    switch(a)
    {
    case 0:
        std::cout << "a = 0" << std::endl;
        break;
    case 1:
        std::cout << "a = 1" << std::endl;
        break;
    }
}

int main()
{
    func(10);
    return 0;
}

1.5 char8_t

编译器版本:GCC 13

文档链接P2513R3: char8_t Compatibility and Portability Fix

新增版本:C++20

主要问题:char8_t类型在不同的平台和编译器之间可能有不同的实现方式和语义,在跨平台开发时,可能会有兼容性和可移植性问题。因此C++23即修复该问题。

如下例:

extern const char* a = u8"a"; // Works in C (using default extensions), broken in C++20
extern const char b[] = u8"b"; // Works in C, broken in C++20
extern const unsigned char* c = u8"c"; // Works in C (using default extensions), broken in C++20
extern const unsigned char d[] = u8"d"; // Works in C, broken in C++20

修改的结果:

①char8_t是基本的字符类型

②确定char8_t和char类型之间的关系(char8_t是utf8字符数据,char是本地字符数据)

③将char8_t类型的标准库扩展到所有操作系统和编译器上

例子:

extern const char* a = u8"a"; // 依然编译不通过
extern const char b[] = u8"b"; // C++23可运行
extern const unsigned char* c = u8"c"; // 依然编译不通过
extern const unsigned char d[] = u8"d"; // C++23可运行

char8_t与char是不同的两种类型,其对应的指针类型也就不能直接赋值,类似于int*和char*。

1.6 wchar_t

编译器版本:GCC 已实现

文档链接P2460R2: Relax requirements on wchar_t to match existing practices

放宽wchar_t要求,使其更加灵活,以匹配已有的实践经验。原来的标准是所有宽编码的所有字符使用一个wchar_t存储,但是实际上在windows(MSVC)一个wchar_t表示一个UTF-16字符。

结果:将已有的实践经验标准化。

2 语义语法

2.1 size_t字面量

编译器版本:GCC 11

文档链接P0330R8: Literal Suffix for (signed) size_t

新增zu作为std::size_t的字面量后缀

例子:

#include <iostream>

int main()
{
    auto a = 10zu;
    auto b = 1u;
    std::cout << std::boolalpha;
    std::cout << std::is_same<decltype(a), decltype(b)>::value << std::endl;  // 输出false
    std::cout << std::is_same<decltype(a), long long>::value << std::endl;  // 输出false
    std::cout << std::is_same<decltype(a), unsigned long long>::value << std::endl; // 输出false
    std::cout << std::is_same<decltype(a), std::size_t>::value << std::endl;  // 输出true
    return 0;
}

2.2 lambda表达式的空圆括号

编译器版本:GCC 11

文档链接P1102R2: Make () more optional for lambdas

lambda表达式中空的圆括号可不写,扩展适用范围(原本是不支持的),包括:

①模板参数、②constexpr、③mutable、④consteval、⑤异常规范和noexcept、⑥属性列表、⑦返回类型、⑧requires

代码例子:

int main()
{
    // noexcept前面的圆括号
    auto a = [] noexcept {}; // C++23以前正确写法:[] () noexcept {}
    // constexpr前面的圆括号
    auto b = [] constexpr {}; // C++23以前正确写法:[] () constexpr {}
    // 返回类型
    auto c = [] -> void {}; // C++23以前正确写法:[] () -> void {}
    return 0;
}

2.3 标识符支持Unicode标准附录31

编译器版本:GCC 12

文档链接P1949R7: C++ Identifier Syntax using Unicode Standard Annex 31

2.4 允许属性重复

编译器版本:GCC 11

文档链接P2156R1: Allow Duplicate Attributes

2.5 向下类型转换为bool类型

编译器版本:GCC 9

文档链接P1401R5: Narrowing contextual conversions to bool

主要是新增支持static_assert和if constexpr表达式中。

2.6 规范行尾反斜杆

编译器版本:GCC 已支持

文档链接P2223R2: Trimming whitespaces before line splicing

规范反斜杆换行,解决多种编译器不一致的问题。

如例子:

#include <iostream>

int main()
{
    int i = 1 
    // \
    + 42
    ; // 按照语义,注释行末尾加反斜杆,下一行应当也是注释行
    std::cout << i << std::endl; // MSVC输出43,GCC和Clang输出1
    return 0;
}

此处规范后,输出i为1

2.7 取消混合的字符串字面量连接语法

编译器版本:GCC 已支持

文档链接P2201R1: Mixed string literal concatenation

在标准层面不再支持混合字符串字面量的连接语法

例子:

int main()
{
    auto a = L"" u""; // 不再支持
    auto a = L"" u8""; // 不再支持
    return 0;
}

2.8 非静态数据成员的地址

编译器版本:GCC 已支持

文档链接P1847R4: Make declaration order layout mandated

明确规定,类的非静态、大小非0的数据成员,声明顺序越往后,其偏移地址越大。最初可追溯到C++03标准不够明确的表述,但其实各大编译器都非常默契地实现了。

不再举例。

2.9 移除垃圾回收的支持

编译器版本:GCC 12

文档链接P2186R2: Removing Garbage Collection Support

于C++11添加最小化的支持,可自行回顾,文档链接N2670: Minimal Support for Garbage Collection and Reachability-Based Leak Detection (revised)

关于垃圾回收的工具,可以自行搜索Boehm GC(可能某些混合使用C++和C#的unity游戏引擎的游戏也依赖于这个库),或者参考其他的一些用C++实现的支持垃圾回收的虚拟机。

移除“安全派生指针”的概念,具体概念可追溯C++11的新特性。

2.10 简化隐式移动语义

编译器版本:GCC 13

文档链接P2266R3: Simpler implicit move

一个表达式是xvalue(消亡值)的条件(之一):

①符合移动条件的变量

②无论是显示和隐式,返回类型是右值引用类型的函数的返回值

③被转换为右值引用

④以xvalue数组为操作数的下标操作

⑤访问一个xvalue对象的非引用类型的非静态数据成员

⑥.*成员指针表达式中,第一个操作数是xvalue,第二个操作数是数据成员

一般来说,有名字的右值引用视为lvalue(左值),没有名字的右值引用视为xvalue(消亡值),函数的右值引用无论是否有名字都视为lvalue(左值)

例子:编译器未支持,待续

2.11 this推导

编译器版本:未支持

文档链接P0847R7: Deducing this

成员函数第一个参数新增支持this,且支持模板推导

例子1-使用:

struct X 
{
    void foo(this X const& self, int i) {}

    template <typename Self>
    void bar(this Self&& self) {}
};

struct D : X { };

void ex(X& x, D const& d) 
{
    x.foo(42); // self绑定到x, i参数值42
    x.bar();  // 函数参数Self类型推导为X&, 实际调用形式是X::bar<X&>
    move(x).bar();  // 函数参数Self类型推导为X, 实际调用形式是X::bar<X>

    d.foo(17);  // self绑定到d
    d.bar(); // 函数参数Self类型推导为D const&, 实际调用形式是X::bar<D const&>
}

例子2-覆盖:

struct B2 
{
    virtual void f() {}
    virtual void g(this B2 const&, int) {}
};

struct D2 : B2 
{
    void f() override {} // 编译通过
    void f(this D2 const&) override {} // 编译报错

    void g(int) const& override {} // 编译报错
    void g(this D2 const&, int) override {} // 编译通过
};

例子3-重载:

struct B3 
{
    virtual void f() {}
};

struct D3 : B3 
{
    void f(this D3&) {} // 编译通过,但并不是覆盖B3::f
};

例子4-代码优化:

//C++23前:->运算符需要写2个函数,用于区别是否const语义
template <typename T>
class Optional1
{
    constexpr T* operator->() 
    {
        return std::addressof(this->m_value);
    }

    constexpr T const* operator->() const 
    {
        return std::addressof(this->m_value);
    }

    int m_value;
};

//新写法:->运算符只需写一个函数
template <typename T>
class Optional2
{
    template <typename Self>
    constexpr auto operator->(this Self&& self) 
    {
        return std::addressof(self.m_value);
    }

    int m_value;
};

例子5-CRTP设计模式:

//C++23前:CRTP设计模式
template <typename Derived>
struct add_postfix_increment1
{
    Derived operator++(int) 
    {
        auto& self = static_cast<Derived&>(*this);

        Derived tmp(self);
        ++self;
        return tmp;
    }
};

struct SomeType1 : add_postfix_increment1<SomeType1> 
{
    SomeType1& operator++() { /*......*/ }
};

//新写法:CRTP设计模式
struct add_postfix_increment2
{
    template <typename Self>
    auto operator++(this Self&& self, int) 
    {
        auto tmp = self;
        ++self;
        return tmp;
    }
};

struct SomeType2 : add_postfix_increment2 
{
    SomeType2& operator++() { /*......*/ }
};

例子6-建造者设计模式:

#include <type_traits>

// C++23前:建造者设计模式
template <typename D=void>
class Builder1
{
    using Derived = std::conditional_t<std::is_void_v<D>, Builder1, D>;
    Derived& self() 
    {
        return *static_cast<Derived*>(this);
    }

public:
    Derived& a() { /* ... */; return self(); }
    Derived& b() { /* ... */; return self(); }
    Derived& c() { /* ... */; return self(); }
};

struct Special1 : Builder1<Special1> 
{
    Special1& d() { /* ... */; return *this; }
    Special1& e() { /* ... */; return *this; }
};

// C++23新特性:建造者设计模式
struct Builder2 
{
    template <typename Self>
    Self& a(this Self&& self) { /* ... */; return self; }

    template <typename Self>
    Self& b(this Self&& self) { /* ... */; return self; }

    template <typename Self>
    Self& c(this Self&& self) { /* ... */; return self; }
};

struct Special2 : Builder2
{
    Special2& d() { /* ... */; return *this; }
    Special2& e() { /* ... */; return *this; }
};

int main()
{
    Builder1().a().b().a().b().c();
    Special1().a().d().e().a();

    Builder2().a().b().a().b().c();
    Special2().a().d().e().a();
    return 0;
}

2.12 更改lambda中的作用域

编译器版本:未支持

文档链接P2036R3: Change scope of lambda trailing-return-type

为消除语义冲突,更改为只有lambda主体部分能够访问捕获列表的参数

核心问题如下例子:

double x;
auto a = [x=1](decltype((x)) y){ return x; };

这个例子可产生4种语义:

①lambda中的x是double&类型。这将导致a(100)报错。

②lambda中的x是int&类型。这将导致a(100.012)报错

③lambda中的x是int const&类型。

④语法错误。

实际是何种语义,全凭编译器决定。

因此,新特性将统一规范lambda。

因编译器未支持,代码例子待续。

2.13 多维下标运算符

编译器版本:GCC 12

文档链接P2128R6: Multidimensional subscript operator

下标运算符支持变长参数,以支持多维访问

用法例子:

#include <iostream>
#include <vector>

struct X
{
    std::vector<int> v;

    // 重载下标运算符,以实现数组元素求和
    template<typename _Index>
    int operator[](_Index &&index)
    {
        return v[index];
    }

    template<typename _T1, typename ... _T2>
    int operator[](_T1 &&index, _T2 &&...indexs)
    {
        return v[index] + (*this)[std::forward<_T2>(indexs)...];
    }
};

int main()
{
    X x;
    x.v = {1, 2, 3, 4, 5, 6, 7};
    int sum = x[1, 2, 3, 6];
    std::cout << sum << std::endl; // 输出16,因为2+3+4+7=16
    return 0;
}

2.14 constexpr函数中常量语境下的变量

编译器版本:GCC 12

文档链接P2242R3: Non-literal variables (and labels and gotos) in constexpr functions

增强编译优化,允许在constexpr函数中的常量环境下定义变量,只要不影响返回值,变量相关的代码将被优化。

例子:

#include <iostream>

template<typename T> 
constexpr bool f() 
{
    // 常量环境下
    if (std::is_constant_evaluated()) 
    {
        // ...
        return true;
    } 
    else 
    {
        T t; // 定义非常量对象,这将被忽略
        t(); // 依然编译通过
        // ...
        return true;
    }
}
struct nonliteral 
{ 
    nonliteral() 
    {
        std::cout << "=======" << std::endl;
    } 

    void operator()()
    {
        std::cout << "===operator()====" << std::endl;
    }
};

// 常量语境下的调用,只要f()函数内部的变量不影响返回值,则编译通过
static_assert(f<nonliteral>()); 

2.15 字符集与字符编码

编译器版本:GCC 10

文档链接P2314R4: Character sets and encodings

新特性将支持以下上下文中使用unicode编码:

⑴asm内联汇编声明语句的编译环境

⑵#include文件名

⑶语言关联

⑷operator “”

⑸#line指令

⑹nodiscard和deprecated属性的提示文本

⑺#error和static_assert的提示文本

⑻__FILE__和__func__的字符串名称

⑼std::typeinfo::name()

⑽字符字面量或字符串字面量

⑾用户自定义的字面量

2.16 一致的字符字面量编码

编译器版本:已支持

文档链接P2316R2: Consistent character literal encoding

无需支持字符集,即可支持完全可移植的编码,并可以在不同的机器上通过编译器的编码转换而保持相同的编码。当前支持unicode编码转义文字。

2.17 初始化声明新增支持别名声明

编译器版本:GCC 12

文档连接:P2360R0: Extend init-statement to allow alias-declaration

选择结构、基于范围的循环结构 的初始化语句新增支持别名声明

例子:

#include <iostream>


int main()
{
    int a[10] = {0};
    int b = 100;
    if(typedef int T; b < 100)
    {
        /* ... */
    }
    for(using T = int; T v : a) 
    {
        std::cout << v << std::endl;
    }
    return 0;
}

2.18 lambda表达式新增属性支持

编译器版本:GCC 9

文档链接P2173R1: Attributes on lambda-expressions

例子:

auto lm = [] [[nodiscard]] ()->int { return 42; };

2.19 复合语句末尾的标签

编译器版本:GCC 13

文档链接P2324R1: Labels at the end of compound statements(C compatibility)

C++与C的标签还存在不兼容的地方,标签可以附加到所有的语句,但是C++不能将标签放到复合语句的末尾。

例子:

void foo(void)
{
first: // 这里的标签C++和C都支持
    int x;
second: // 这里的标签C++和C都支持
    x = 1;
last: // 这里最末尾的标签C支持,但C++不支持
}

int main()
{
    int a = 10;
    // 这里的标签C++支持,但C不支持,C需要用花括号
    if(a > 10)
test_label1: int x; 

    // 新版写法:
    if(a > 10)
    {
test_label2: 
        int x; 
    }
    return 0;
}

2.20 重写==和!=运算符

编译器版本:GCC 13

文档链接P2468R2: The Equality Operator You Are Looking For

重载operator==(A, B)时

①如果未重载operator!=(A, B),则对operator==(B, A)、operator!=(A, B)、operator!=(B, A)生效。

②如果已重载operator!=(A, B),则对operator==(B, A)、operator!=(A, B)、operator!=(B, A)不生效。

③如果已重载operator==(B, A)、operator!=(B, A),则优先调用operator==(B, A)、operator!=(B, A)。

但对于operator!=(A, B)没有这样的效果

代码如下:

#include <type_traits>
#include <iostream>

struct A {};

template<typename T> 
bool operator==(A, T)   // #1
{ 
    std::cout << "bool operator==(A, T)" << std::endl;
    return true; 
}

bool a1 = 0 == A();  // ok,调用#1
bool a2 = 0 != A();  // ok,调用#1
bool a3 = A() == 0;  // ok,调用#1
bool a4 = A() != 0;  // ok,调用#1

template<typename T> 
bool operator!=(A, T)  // #2
{ 
    std::cout << "bool operator!=(A, T)" << std::endl;
    return true;
}

bool a5 = 0 == A();  // 编译错误,没有找到operator==(int, A)
bool a6 = 0 != A();  // 编译错误,没有找到operator!=(int, A)
bool a7 = A() == 0;  // ok,调用#1
bool a8 = A() != 0;  // ok,调用#2

struct B 
{
    bool operator==(const B&)   // #3
    { 
        std::cout << "bool B::operator==(const B&)" << std::endl;
        return true; 
    }
};

struct C : B 
{
    C() {}
    C(B) {}
    bool operator!=(const B&)   // #4
    { 
        std::cout << "bool C::operator!=(const B&)" << std::endl;
        return true; 
    } 
};

bool c1 = B() == C();  // ok,调用#3
bool c2 = C() == B();  // C对象的operator==(const B&) 和 B对象的operator==(const B&)冲突
                       // GCC 13编译不会报错,但有警告

struct D {};

template <typename T>
bool operator==(D, T) // #5
{ 
    std::cout << "bool operator==(D, T)" << std::endl;
    return true; 
}

inline namespace N 
{
    template <typename T>
    bool operator!=(D, T) // #6
    { return true; }
}

bool d1 = 0 == D(); // 编译错误,当前命名空间下,operator==(D, int)和operator!=(D, T)都已重载
                    // 找不到operator==(int, D)的重载实现


2.21 移除无法编码的宽字符字面量和多字符宽字符字面量

编译器版本:GCC 13

文档链接P2362R3: Remove non-encodable wide character literals and multicharacter wide character literals

这篇文档不是不再支持宽字符字面量语法,而是建议如何移除无法编码的情况,使代码更加健壮和易读。

例子1:

#include <iostream>

int main()
{
    std::cout << 'ab' << std::endl;// a是0x61,b是0x62,所以输出24930(即0x6162)
    // 因此,建议拆分成'a'和'b'
    return 0;
}

例子2:

#include <iostream>


int main()
{
    // 类似这种unicode字符集的宽字符,可以使用L前缀来表示宽字符
    std::wcout << L'\u0001F525' << std::endl;
    return 0;
}

2.22 新的八进制、十六进制和universal-character-name的转义序列

编译器版本:GCC 13

文档链接P2290R3: Delimited escape sequences

universal-character-name转义序列:即使用 4或8个十六进制数字16或32位 来表示的unicode标量值

新的转义字符表示方法:

const char *ch1 = "\u{0001F1F8}"; // unicode
const char *ch2 = "\o{053724}"; // 八进制
const char *ch3 = "\x{0001F1F8}"; // 十六进制

2.23 新增unicode编码的字符序列的转义序列

编译器版本:GCC未实现

文档链接P2071R2: Named universal character escapes

新增 \N{名称} 语法来标识标准unicode字符序列,暂时只适用于字符和字符串。

例子:

const char *ch = "\N{0001F1F8}\N{U+000100}";

2.24 常量表达式中使用未知的指针和引用

编译器版本:GCC 未实现

文档链接P2280R4: Using unknown pointers and references in constant expressions

本质的改动,就是把 可以在编译期计算出结果的运行期变量 在编译期计算出来而且不用写constexpr等修饰词。

例子:

int func1(int a)
{
    return a + a;
}

int func2(int *a)
{
    return (*a) * (*a);
}

int main()
{
    int a = 10;
    int b = 2;
    int c = func1(b); // 此处因为b可在编译期计算得到2,所以c的结果可直接在编译期得到4
    int d = func2(&a); // 此处因为b可在编译期计算得到10,所以c的结果可直接在编译期得到100
    return 0;
}

2.25 static operator()

编译器版本:GCC 13

文档链接P1169R4: static operator()

目前的括号运算符重载函数都是以非静态成员函数的方式实现,而对于STL中的接口,需要传入带有括号运算符函数的类型(例如std::less等),如果该类型的括号重载函数没有内联,那么在使用时还得必须创建对应类型的对象,也就需要使用一个额外的寄存器存入对象的this指针。

例子:

#include <vector>
#include <algorithm>

struct A
{
    static bool operator()(int a, int b)
    { return a > b; }
};

int main()
{
    std::vector<int> v;
    std::sort(v.begin(), v.end(), std::greater<int>());
	
    using CmpType = decltype(&A::operator()); // CmpType被推导为std::function<bool(int, int)>
    std::sort(v.begin(), v.end(), CmpType()); // C++23支持的形式
    return 0;
}

汇编的结果可自行查看,静态的operator()相比于非静态的operator(),少了一层偏移量的设定。

2.26 新增浮点数类型别名

编译器版本:GCC 13

文档链接P1467R9: Extended floating-point types and standard names

新增类型:std::float16_t、std::float32_t、std::float64_t、std::float128_t

分别代表16位、32位、64位、128位的浮点数,不再赘述

2.27 从继承的构造函数中推导模板参数

编译器版本:未实现

文档链接P2582R1: Wording for class template argument deduction from inherited constructors

在C++17中就可以利用构造函数来推导模板参数(请自行回顾),在C++23中,这种办法扩展到了继承机制,可以从基类继承过来的构造函数中推导模板参数

例子:

template<typename T> 
struct B 
{
    B(T) {}
};

template<typename T> 
struct C : public B<T> 
{
    using B<T>::B;
};

template<typename T> 
struct D : public B<T> 
{ };

C c(42); // 编译通过,c类型推导为C<int>
D d(42); // 编译不通过,没有引入可推导的构造函数
B(int) -> B<char>; // 显式声明推导类型
C c2(42); // 编译通过,c2对象被推导为C<char>

template<typename T> 
struct E : public B<int> 
{
    using B<int>::B;
};

/**
 * 编译不通过,因为上面B(int)已经显式声明推导类型是B<char>了
 * E中using的是B<int>类型的构造函数
 * 没有引入可推导的路径
 */ 
E e(42);

E<int> e2(42); // 编译通过

template<typename T, typename U, typename V> 
struct F 
{
    F(T, U, V) {}
};

template<typename T, typename U> 
struct G : F<U, T, int> 
{
    using G::F::F;
};

G g(true, 'a', 1); // 编译通过,g对象推导为G<char, bool>

2.28 支持UTF8作为可移植的源文件编码

编译器版本:GCC 13

文档链接P2295R6: Support for UTF-8 as a portable source file encoding

2.29 显式生命周期管理

编译器版本:编译器未实现

文档链接P2590R2: Explicit lifetime management

个人理解,思想上类似一个内存池(并非对象池的那种内存池),当需要创建对象(无论什么类型)时,都可以直接从同一个内存池中获得内存地址。

因编译器没实现,就不多说了。

2.30 static operator[]

编译器版本:GCC 13

文档链接P2589R1: static operator[]

operator[]增加支持static修饰,略

2.31 允许constexpr函数中定义static constexpr变量

编译器版本:GCC 13

文档链接P2647R1: Permitting static constexpr variables in constexpr functions

比较简单,直接看例子:

constexpr int func(int a, int b)
{
    static constexpr int v = 100; // C++23起支持static constexpr
    return a + b + v;
}

2.32 consteval向上传导

编译器版本:编译器未实现

文档链接P2564R3: consteval needs to propagate up

待续

2.33 更有意义的export

编译器版本:编译器未实现

文档链接P2615R1: Meaningful exports

待续

2.34 修复基于范围的for循环的问题

编译器版本:编译器未实现

文档链接P2718R0: Wording for P2644R1 Fix for Range-based for Loop

待续

3 预处理指令

3.1 elifdef和elifndef

编译器版本:GCC 12

文档链接P2334R1: Add support for preprocessing directives elifdef and elifndef

新增预处理命令,用于ifdef和ifndef的else部分的条件。

例子:

#ifdef MY_IF1
#elifdef MY_IF2
#endif

#ifndef MY_IF1
#elifndef MY_IF2
#endif

#ifdef MY_IF1
#elifndef MY_IF2
#endif

#ifndef MY_IF1
#elifdef MY_IF2
#endif

3.2 warning

编译器版本:GCC 13

文档链接P2437R1: Support for #warning

用于在预处理阶段放出警告

语法格式:#warning [text]

例子:

#warning "ahhahaha"

4 属性

4.1 assume

编译器版本:GCC 13

文档链接P1774R8: Portable assumptions

新增编译器指令,用于提示编译器某一个假设条件一定成立,可以忽略某些可能发生的错误检查,以便编译器更好地优化代码

语法格式:[[assume(expr)]]

其中expr为可得到bool的表达式

用法例子:

[[assume(expr1, expr2)]]; // Error
[[assume((expr, expr2))]]; // OK
[[assume(x = 1)]]; // Error
[[assume(x == 1)]]; // OK
[[assume((x = 1))]]; // OK

优化例子:

int f1(int x) 
{
    [[assume(x >= 0)]]; // 假设x是不小于0的
    return x / 32; // 可能会省略负值的处理
}

int f11(int x) 
{
    return x / 32; // 可能会省略负值的处理
}

int f2(int y) 
{
    [[assume(++y == 43)]]; // 假设y+1等于43
    return y; // 该return语句可能被替换为return 42;
}

int f22(int y) 
{
    return y; // 该return语句可能被替换为return 42;
}

对应的汇编代码(开启-O2编译选项,可以看得到优化效果)

f1(int):
        mov     eax, edi
        sar     eax, 5
        ret
f11(int):
        test    edi, edi
        lea     eax, [rdi+31]
        cmovns  eax, edi
        sar     eax, 5
        ret
f2(int):
        mov     eax, 42
        ret
f22(int):
        mov     eax, edi
        ret

5 标准库

推荐网站:https://www.apiref.com/cpp-zh/cpp/header.html
不再列举

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++23新特性个人总结 的相关文章

  • 结构化绑定中缺少类型信息

    我刚刚了解了 C 中的结构化绑定 但有一件事我不喜欢 auto x y some func is that auto正在隐藏类型x and y 我得抬头看看some func的声明来了解类型x and y 或者 我可以写 T1 x T2 y
  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 无限循环与无限递归。两者都是未定义的吗?

    无副作用的无限循环是未定义的行为 看here https coliru stacked crooked com view id 24e0a58778f67cd4举个例子参考参数 https en cppreference com w cpp
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l

随机推荐

  • STM32移植U8g2图形库——玩转OLED显示

    之前的文章 介绍过ESP8266在Arduino IDE环境中使用U8g2库 实现OLED上的各种图形显示 本篇 介绍一下U8g2库如何移植到STM32上 进行OLED的图形显示 本次的实验硬件为 STM32 型号为最常见的STM32F10
  • 前事不忘,后事之师——基于相似性进行剩余有效寿命预测的案例讲解

    在上一篇文章中我们讲到了三种机电产品算命方法 相似模型法 退化模型法和生存模型法 这一篇我们将使用相似模型法构建完整的剩余使用寿命 RUL 估计工作流程 该案例来自MATLAB的Similarity Based Remaining Usef
  • JavaScript设计模式(三)——单例模式、装饰器模式、适配器模式

    个人简介 个人主页 前端杂货铺 学习方向 主攻前端方向 正逐渐往全干发展 个人状态 研发工程师 现效力于中国工业软件事业 人生格言 积跬步至千里 积小流成江海 推荐学习 前端面试宝典 Vue2 Vue3 Vue2 3项目实战 Node js
  • SpringBoot快速入门

    SpringBoot快速入门 1 SpringBoot简介 SpringBoot概述 SpringBoot起步依赖 SpringBoot程序启动 入门案例 SpringBoot项目快速启动 2 基础配置 自动提示功能消失解决方案 yaml语
  • docker创建多个mysql集群_Docker在一台服务器上安装和配置Mysql集群

    1 从docker hub下载mysql5 6的镜像 docker pull mysql 5 6 2 使用mysql5 6镜像运行4台mysql服务 用端口号区分 前期准备工作 在本机创建四个目录 分别用了存储4台mysql服务的数据 日志
  • Windows服务器管理(运维)——cmd命令大全

    1 文件和目录操作命令 cd 更改当前目录 dir 列出当前目录中的文件和文件夹 mkdir 创建一个新的文件夹 rmdir 删除一个空的文件夹 copy 复制文件或文件夹 del 删除文件 ren 重命名文件或文件夹 move 移动文件或
  • angular html原理,Angular 4.x ngModel 双向绑定原理揭秘

    在 Angular 4 x 中对于使用 Template Driven 表单场景 如果需要实现表单数据绑定 我们就需要引入 ngModel 指令 该指令用于基于 domain 模型 创建 FormControl 实例 并将创建的实例绑定到表
  • java日志级别

    java中日志级别有7 个级别 severe Warning info config fine finer finest 默认情况只记录前三个级别 另外可以使用Level ALL开启所有的级别记录 或者使用Level OFF关闭所有的级别记
  • android动静态申请IMEI或其他特殊权限(适配11)

    报错原因 今天又是撸代码的一天 人生第一个项目上架闪退被打回 很难受 打开就闪退 后面才恍然大悟 打开APP默认申请获取手机IMEI 测试用的手机被我手动打开了权限 所以一直没有注意这个问题 果然 log报错 java lang Secur
  • pjsip的一个qt写的demo

    msvc版本编译的pjsip的demo 有源码 也有可直接运行的包 本程序解决了pjsip双方互相同时呼叫时会出现的问题 目前只是用来呼叫接听的demo 没有做流媒体传输 https download csdn net download q
  • 【C语言】使用C语言实现静态、动态的通讯录(简单易懂)

    我们在学习结构体之后 就可以尝试去实现通讯录的制作 如果您这边对于结构体还没有太多的认识的话 请先访问这一篇文章 会有利于接下来的学习 自定义类型 带你走进结构体 枚举 联合 小王学代码的博客 CSDN博客 目录 一 通讯录 二 静态通讯录
  • Java自增和自减运算符(++和--)

    在对一个变量做加 1 或减 1 处理时 可以使用自增运算符 或自减运算 或 是单目运算符 放在操作数的前面或后面都是允许的 与 的作用是使变量的值增 1 或减 1 操作数必须是一个整型或浮点型变量 自增 自减运算的含义及其使用实例如表 1
  • Flutter实现倒计时功能,秒数转时分秒,然后倒计时

    Flutter实现倒计时功能 发布时间 2023 05 12 本文实例为大家分享了Flutter实现倒计时功能的具体代码 供大家参考 具体内容如下 有一个需求 需要在页面进行显示倒计时 倒计时结束后 做相应的逻辑处理 实现思路 在Flutt
  • 牛客中等难度3

    HJ70 矩阵乘法计算量估算 描述 矩阵乘法的运算量与矩阵乘法的顺序强相关 例如 A是一个50 10的矩阵 B是10 20的矩阵 C是20 5的矩阵 计算A B C有两种顺序 AB C 或者 A BC 前者需要计算15000次乘法 后者只需
  • 异常处理包装技术

    异常大致可分为两种 受检查异常和非受检查异常 受检查异常是在编译期间就可以检查到的 非受检查异常又分为error和RuntimeException 非受检查异常是可控的 可以人为操作修改的 一般我们针对业务异常 非受检查异常 进行处理 会继
  • 鸿蒙系统是否可以用来做服务器,小米手机也能使用鸿蒙系统?国内厂商使用鸿蒙热情高涨...

    原标题 小米手机也能使用鸿蒙系统 国内厂商使用鸿蒙热情高涨 自从6月2日HarmonyOS 2正式发布以来 国内针对鸿蒙系统的热议一直不减 对于新买的华为手机用户来说 能第一批次使用上国产的手机系统 也确实过了一把瘾 本次手机系统更新 华为
  • Android蓝牙开发教程(三)——蓝牙设备相互通讯

    在上一篇中已经介绍如何连接我们搜索到的蓝牙设备 如果你还没阅读过 建议先看看上一篇文章Android蓝牙开发教程 二 连接蓝牙设备 在上一篇文章中 无论是自动连接还是被动连接 连接成功后 都是将获取到的BluetoothSocket交由连接
  • 根据眼动数据的模板作为KNN聚类的中心点并因此进行数据分类

    from scipy io import loadmat import numpy as np import matplotlib pyplot as plt 实验数据采集分为两个过程 第一个是眼动校准阶段 要求实验参与者依次观看界面上的数
  • VMWare 6.5.3 绿色精简版汉化 +VMware Workstation 6.5.3 Build 185404 汉化绿色精简版

    绿色精简版 参考网上6 5 X几个绿色精简版更新制作 bat不加密 不加入个人信息 喜欢研究的随便看 精简版一般使用够用了 高手估计会觉得缺少某些功能了 那就只能装完整版了 bridge 桥接 usb服务 host only都可以使用 VM
  • C++23新特性个人总结

    文章目录 1 关键字 1 1 consteval 1 2 auto 1 2 1 新增支持数组指针的引用类型 1 2 2 代替decay copy语义 1 3 volatile 1 4 constexpr 1 5 char8 t 1 6 wc