C++17新特性


C++17新特性

前言

前項目C++使用的時C++14標准,考慮到與時俱進,C++20也基本上確定,不過由於目前使用的linux(uos)上的自帶gcc並不能全面支持C++20, 所以暫時考慮新項目上C++17新標准,並且寫一篇完整的C++17中項目可能用到的新特性簡單文檔


visual studio 2019需要添加編譯參數 /std:c++17

If Statements with Initializer

帶初始化器的if語句

example

std::map<std::string, int> map;
map["nihao"] = 1;
map["shijie"] = 2;
if (auto ret = map.begin(); ret != map.end()) {
  std::cout << ret->first << ": " << ret->second;
}

Constexpr if

constexpr if 語句中,條件 的值必須是可按語境轉換到 bool 類型的經轉換常量表達式。若其值為 true,則舍棄 false分支語句(若存在),否則舍棄 true分支語句。

通常我們寫業務很難用到,在模板元編程中會特別有用

example

template <typename T>
void TestConstexprIf(T value) {
    if constexpr (std::is_integral_v<T>)
        std::cout << "is integral" << std::endl;
    else
        static_assert(false, "T必須是整型");
}

inline 變量

inline 變量用來解決一個問題,全局變量再頭文件中定義之后多出使用產生符號重定義錯誤

錯誤例子

// test.h 頭文件
int test = 10;

// test1.cpp 
void Function1() {
  test = 20;
}

// test2.cpp
void Function() {
  test = 30;
}


// 上面的代碼編譯將會產生重定義錯誤,c++17之前解決方案是使用extern導出全局變量

// 解決方案

// test.h 頭文件
extern int test;

// test.cpp
int test = 10;

C++17 之后引入inline變量使其全局變量可以直接再頭文件中聲明定義

example

inline int test = 10;

嵌套命名空間

example

namespace test::test2 {
int i = 0;
}

std::cout << test::test2::i << std::endl;

屬性說明符

屬性為各種由實現定義的語言擴展(例如 GNUIBM 的語言擴展 __attribute__((...)),微軟的語言擴展 __declspec() 等)提供了統一化的語法。

[[fallthrough]] 標准屬性

指示從前一標號直落是有意的,而在發生直落時給出警告的編譯器不應診斷它。

C++17 之前的標准下, 有如下代碼

switch (device.status())
{
case sleep:
   device.wake();
   // fall thru
case ready:
   device.run();
   break;
case bad:
   handle_error();
   break;
}

C++17 可以這樣寫

switch (device.status())
{
case sleep:
   device.wake();
   [[fallthrough]];
case ready:
   device.run();
   break;
case bad:
   handle_error();
   break;
}

再之前的代碼編譯器會告訴你沒有break的警告,但是再c++17中使用fallthrough屬性就可以消除這個警告了

[[maybe_unused]] 標准屬性

可用來消除未使用的函數和變量編譯器所發出的警告

example

[[maybe_unused]] bool testUnusedVariable = true;

[[maybe_unused]] 
void TestUnusedFunction() {
  
}

[[nodiscard]] 標准屬性

如果你的某個函數的返回值特別重要,希望使用者不要忽略,可以添加這個屬性,再編譯的時候如果函數使用者沒有使用返回值將會有一個警告產生

example

[[nodiscard]] bool TestNodiscard() {
    return true;
}

[[deprecated]] 標准屬性

提示允許使用聲明有此屬性的名稱或實體,但因為一些原因不鼓勵使用,一般用在即將廢棄的函數,但是還有老的用戶使用到了這個函數

example

// 再vs中這個會不是警告而是錯誤.
[[deprecated("test deprecated")]] bool TestDeprecated() {
    return true;
}

[[noretrun]] 標准屬性

告知函數並沒有返回值

example

[[noreturn]] void TestNoreturn() {

}

string_view

C++17 中特別新增的一個特別好用且重要的特性,string_view相對於string來說就是一個只讀的stringstring_view的賦值操作的空間成本和時間成本遠遠勝於stringstring_view的賦值特別像一個指針的賦值,一般來說再一下情況下使用string_view會更合適

exampel

// 常量string
const std::string = "hello world";
// string_view 更為合適
const string_view = "hello world";

// 函數參數
void Function1(const std::string& arg1) {

}

// string_view 更為合適
void Function1(string_view arg1) {

}

filesystem

在沒有C++17時一直就使用experiment/filesystem,在C++17 filesystem被正式納入C++標准庫中, 由於大多數人對filesystem都比較熟悉了,在這里只是簡單的介紹一下

example

std::filesystem::path path("testpath");
if (std::filesystem::exists(path)) {
  // 存在
} else {
  // 不存在
}

any

any是一個可用於任何類型單個值的類型安全容器,如果你之前有了解過boost相信對這個any類已經非常熟悉了

example

std::any Int = 69;
std::any Double = 69.123;
std::any String = std::string_view("Hello");

std::cout << Int.type().name() << std::endl;
std::cout << Double.type().name() << std::endl;
std::cout << Double.type().name() << std::endl;

std::vector<std::any> anys = { Int, Double, String };
std::cout << std::any_cast<int>(Int) << std::endl;
std::cout << std::any_cast<double>(Double) << std::endl;
std::cout << std::any_cast<std::string_view>(String) << std::endl;

    // has_value: 是否有值
std::any a = 1;
if (a.has_value()) {
  std::cout << a.type().name() << std::endl;// i
}

// reset:清空容器
a.reset();
if (a.has_value()) {
  std::cout << "no value\n";// no value
}

optional

熟悉boost的也應該非常熟悉optional了,它最常用的地方是在你返回值是string或者int等出現錯誤之后非常隱式的表達的地方,使用std::optional就可以幫你解決這種問題

example

[[nodiscard]]
std::optional<int> TestOptional() {
    // 之前我們可能需要使用return -1,代表錯誤,現在使用st::optional就不需要那種太過於隱式的表達
    if (true) {
        return 9999;
    } else {
        return std::nullopt;
    }
}

[[nodiscard]]
std::optional<std::string> TestOptional2() {
    // 之前我們可能需要使用return -1,代表錯誤,現在使用st::optional就不需要那種太過於隱式的表達
    if (true) {
        return "helloworld";
    } else {
        return std::nullopt;
    }
}

// optional 
auto result = TestOptional();
if (result.has_value()) {
  // 有值,代表成功
} else {
  // result沒有值代表失敗
}

// 這個value_or表示當TestOptional的返回值為nullopt時使用or里面的值
auto ret = TestOptional2().value_or("");

variant

variant用來表示一個類型安全的聯合體,variant 的一個實例在任意時刻要么保有其一個可選類型之一的值,要么在錯誤情況下無值。
variant 不容許保有引用、數組,或類型 void, 空variant可以使用std::variant<std::monostate>


下面這個例子是一個實際接口設計時利用std::variant解決不同類型不同參數的接口
// variant
struct SystemProxyConfig {
  bool isService;
};

struct CustomProxyConfig {
  bool isFile;
  std::string pathOrContent;
};
std::variant<SystemProxyConfig, CustomProxyConfig> config;
//config = CustomProxyConfig{ false, "http://192.168.21.161/spiderweb.pac" };
config = SystemProxyConfig{ false };
if (std::get_if<CustomProxyConfig>(&config)) {
  // 類型成功
  CustomProxyConfig customConfig = std::get<CustomProxyConfig>(config);
} else {
  // 類型失敗
  SystemProxyConfig systemProxyConfig = std::get<SystemProxyConfig>(config);
  int i = 0;
}

execution

executionC++STL算法庫提供了一種算法的執行策略設置,目前支持的策略:

  • sequenced_policy (順序執行策略)
  • parallel_policy (並行執行策略)
  • parallel_unsequenced_policy (並行及無序執行策略)
  • unsequenced_policy (無序執行策略)

example

    std::vector<int> testExecution{ 1, 2, 3, 4, 5,8, 19, 20 ,30,40,50,0,102,40,10,30,20,1000,32,31,34,45};
    auto it1 = std::find(std::execution::seq, testExecution.begin(), testExecution.end(), 5);
    auto it2 = std::find(std::execution::par, testExecution.begin(), testExecution.end(), 5);
    auto it3 = std::find(std::execution::par_unseq, testExecution.begin(), testExecution.end(), 5);

參考鏈接

cppreference.com

fallthrough屬性


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM