C++20語言核心特性的變化

ChebyshevTST發表於2023-11-30

using for Enumeration Values

  對比一下C++20前後的區別:

enum class State {
    open,
    progress,
    done = 9
};
// Before C++20
void print(State s) {
    switch (s) {
        case State::open:
            std::cout << "open\n";
            break;
        case State::done:
            std::cout << "done\n";
            break;
        case State::progress:
            std::cout << "progress\n";
            break;
        default:
            assert(nullptr);
    }
}
// Since C++20
void print1(State s) {
    using enum State;
    switch (s) {
        case open:
            std::cout << "open\n";
            break;
        case done:
            std::cout << "done\n";
            break;
        case progress:
            std::cout << "progress\n";
            break;
        default:
            assert(nullptr);
    }
}

void print2(State s) {
    using State::open, State::done, State::progress;
    switch (s) {
        case open:
            std::cout << "open\n";
            break;
        case done:
            std::cout << "done\n";
            break;
        case progress:
            std::cout << "progress\n";
            break;
        default:
            assert(nullptr);
    }
}

 

Range-Based for Loop with Initialization

  基於範圍的for迴圈是C++11引入的新特性,自C++20起,基於範圍迴圈也可以進行初始化。

int main() {
    std::vector v{1, 2, 3};
    for (int i{1}; const auto& item : v)
        std::cout << std::format("{}: {}\n", i++, item);
}

 

Feature Test Macros

  特性測試宏,正如其名,是為了當前版本編譯器是否支援某個語言特性。這個宏以__cpp為字首。

#if __cpp_generic_lambdas >= 201707
    // generic lambdas with template parameters can be used
#endif

#ifndef __cpp_lib_as_const
template <typename T>
const T& as_const(T& t) {
    return t;
}
#endif

 

Attribute [[no_unique_address]]

#include <iostream>

struct Empty {};

struct I {
    int i;
};

struct S {
    Empty e;
    int i;
};

int main() {
    std::cout << "sizeof(Empty): " << sizeof(Empty) << '\n';
    std::cout << "sizeof(I): " << sizeof(I) << '\n';
    std::cout << "sizeof(S): " << sizeof(S) << '\n';
}

  空類為了區分不同物件的地址,位元組大小是1;而結構體S由於記憶體對齊的原因,所以位元組大小是8。輸出結果毫無疑問是1,4,8。

#include <iostream>

struct Empty {};

// EBCO
struct S : Empty {
    int i;
};

// no_unique_address
struct S2 {
    [[no_unique_address]] Empty e;
    int i;
};

int main() {
    std::cout << "sizeof(Empty): " << sizeof(Empty) << '\n';
    std::cout << "sizeof(S): " << sizeof(S) << '\n';
    std::cout << "sizeof(S2): " << sizeof(S2) << '\n';
}

  註解標籤no_unique_address能起到和空基類最佳化相同的效果。

 

Attributes [[likely]] and [[unlikely]]

  在if/else,switch分支當中都可以使用,幫助編譯器作分支預測的最佳化。

int f(int n) {
    if (n <= 0) [[unlikely]] {
        return n;
    } else {
        return n * n;
    }
}

int g(int n) {
    if (n <= 0) {
        return n;
    } else [[likely]] {
        return n * n;
    }
}

int h(int n) {
    switch (n) {
        case 1:
            //
            break;
        [[likely]] case 2:
            // 
            break;
    }

    return 0;
}

 

Attribute [[nodiscard]] with Parameter

  nodiscard用於修飾函式,當被修飾的函式發生呼叫(僅僅呼叫而不作賦值或者強制轉換操作),編譯器會報警告資訊。

[[nodiscard]]
int f(int n) {
    return n;
}

int main() {
    f(1); // Warning
    int n = f(1); // OK
    reinterpret_cast<int *>(f(1)); // OK
}

 

New Character Type char8_t

    using namespace std::literals;

    auto c = u8'c'; // char8_t
    auto s = u8"Hello World"; // const char8_t *
    auto str1 = u8"Hello World"s; // std::u8string
    auto str2 = u8"Hello World"sv; // std::u8string_view

    std::cout << u8'c' << '\n'; // OK in C++17, error in C++20
    std::cout << u8"Hello World\n"; // OK in C++17, error in C++20
    std::cout << u8"Hello World\n"s; // OK in C++17, error in C++20
    std::cout << u8"Hello World\n"sv; // OK in C++17, error in C++20

    std::cout << c << '\n'; // OK in C++17, error in C++20
    std::cout << s << '\n'; // OK in C++ 17, error in C++20
    std::cout << str1 << '\n'; // OK in C++17, error in C++20
    std::cout << str2 << '\n'; // OK in C++17, error in C++20

  更多的介紹將在下次帶來,感謝支援?