0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

从C++11升级至C++17,它们让嵌入式系统更好了!

技术让梦想更伟大 来源:CSDN 2023-06-13 14:34 次阅读
关注、星标公众号,直达精彩内容54f07d9e-099b-11ee-962d-dac502259ad0.png

		

2011 年8 月,ISO 委员会发布了 C++11,2017 年 12 月又发布了 C++17 标准,每次编程语言新版本的迭代,会令不少团队也开始着手升级开发环境,例如本文作者。那么从 C++11 升级到 C++17,究竟有哪些特别的变化值得关注?

最近,我们团队正在升级开发环境,尝试使用许多工具和编程语言的新版本。在这个过程中,比较困难的一项工作是将我们的嵌入式应用程序的代码库从 C++11 升级到 C++17。

在本文中,我将展示在嵌入式世界中非常有用的一些C++17 的特性(注意:从 C++11 迁移到 C++17 也涵盖了 C++14,因此我也会提到 C++14 的一些特性)

看完整的C++17性列表,可前往:https://github.com/AnthonyCalandra/modern-cpp-features#c17-language-features。

C++14 的主要变化

当初,我们从 C++03 迁移到了 C++11,与之相比,从 C++11 升级到 C++14 时看到的升级比较小。因此,可以在嵌入式系统中使用的 C++14 特有功能实际上并不多。

二进制字面量

如果你经常需要执行按位运算或修改寄存器,那么一定很喜欢这些字面量。一些编译器具有支持此类字面量的扩展,这些字面量在实际的标准中也有一席之地。

uint8_t a = 0b110; // == 6
uint8_t b = 0b1111'1111; // == 255

constexpr**

在 C++14 中,可以在 constexpr 函数中使用的语法得到了扩展。constexpr 特别适用于嵌入式开发,因为它可以在编译时进行计算并将一些代码简化为常量。请注意,只有当表达式的所有需求都可以在编译期间确定时,才能在编译时计算表达式。

constexpr int factorial(int n) {
if (n <= 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
factorial(5); // == 120 (Calculated at compile time)

C++ 17 的世界

与 C++14 相比,C++17 标准有了很大的变化,但无需担心,你仍然可以使用已有的功能。除了已有功能之外,你还将拥有更强大的 C++17 语法和库。

(1)属性

首先,我们来介绍三个新属性:[[fallthrough]]、[[nodiscard]] 和 [[maybe_unused]]。因为这些属性只在编译时考虑,所以你根本不需要担心它们的效率。它们的存在就是为了提升代码开发。

[[fallthrough]]

你可以利用这个属性将两个相邻的 case 分支的主体合并到一个 switch 中,而不会收到来自编译器的任何警告。你可以通过这个属性告诉编译器前一个case主体结束是有意为之。

switch (n) {
case 1: [[fallthrough]]
// ...
// no `break;`
case 2:
// ...
break;
}

[[nodiscard]]

你是不是也经常忘记检查函数的返回值?有了这个属性,丢弃返回值就会收到编译器的警告。

[[nodiscard]] bool do_something() {
return is_success; // true for success, false for failure
}
do_something(); /* warning: ignoring the return value of function declared with attribute 'nodiscard' */

[maybe_unused]]

为了避免收到警告,必须将未使用的变量转换为 void,你是不是也感到不耐烦?试试看这个属性,你就可以摆脱那些烦人的警告。

void my_callback(std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log(msg);
}

(2)编译时的力量

编译时的检查是我最喜欢 C++ 的地方。在 C++17 中,这种能力通过一些新特性得到进一步增强。想一想许多嵌入式系统中繁琐的调试过程,如今甚至不需要部署代码就可以检查结果,是不是觉得是个特大好消息?传输可执行文件、准备环境和测试等一系列工作都非常艰巨,而且很耗时。但使用编译时编程,这部分头疼的工作都可以省略。

没有消息的静态断言

你可能认为,我们已经有了 static_assert(..),可以在编译时进行检查。而如今,断言机制甚至不需要错误消息。这样,代码看上去会更加清晰。

static_assert(false);

if constexpr

我最喜欢的一个语句!我们可以利用 if constexpr 编写一些代码,这些代码可以根据编译时的条件,有选择地进行实例化。

template<typename T>
auto length(const T& value) noexcept {
if constexpr (std::integral::value) { // is number
return value;
}
else {
return value.length();
}
}
int main() noexcept {
int a = 5;
std::string b = "foo";
std::cout << length(a) << ' ' << length(b) << '
'; // Prints "5 3"
}

在 C++17 之前,上面这段代码需要编写两个不同的函数,分别用于字符串和整数输入,如下所示。

int length(const int& value) noexcept {
return value;
}
std::size_t length(const std::string& value) noexcept {
return value.length();

constexpr lambda

如果你也喜欢在代码中使用 lambda 表达式,那么肯定会喜欢这个功能。此外,Lambdas 的调用也可以采用直接声明为 constexpr 的形式。

auto identity = [](int n) constexpr { return n; };
static_assert(identity(123) == 123);

(3)语法糖

在 C++17 中,有一些功能可以帮助你编写更漂亮的代码。即使它们的存在对运行时性能没有明显的影响,但你会很喜欢它们。

折叠表达式

如果你有过使用可变参数模板来编写具有可变输入或迭代次数的递归算法的经历,那么就可能遇到必须为该可变参数模板函数实现终止符的问题。例如,下面的代码是用 C++11 编写的,作用是累加给定的数字。

int sum() { return 0; } // Termination function
template<typename ...Args>
int sum(const int& arg, Args... args) {
return arg + sum(args...);
}

如果我们没有实现不接受任何输入的终止符,这段代码将无法通过编译。但有了折叠表达式,你就不必实现终止符了,而代码看上去也更好,如下所示。

template<typename ...Args>
int sum(Args&&... args) {
return (args + ...);
}

嵌套命名空间

不知道为什么 C++ 委员会以前没有想到这一点。无需多说,分别看下面 C++11 和 C++17 中嵌套命名空间的定义,你就能发现区别。

// C++11
namespace A {
namespace B {
namespace C {
int i;
}
}
}
// C++17
namespace A::B::C {
int i;
}

加强版的条件语句

如果所有条件语句都像 for 语句一样具有初始化,那是不是更强大?在 C++17 中,条件语句也增加了初始化部分。

这是迄今为止我所见过的最强大的功能之一,因为你无需在输入一系列 if-else 语句或 switch-case 之前,编写一堆局部变量。

if (int i = 4; i % 2 == 0) {
cout << i << " is even number" << endl;
}
switch (int i = rand() % 100; i) {
default:
cout << "i = " << i << endl;
break;
}

内联变量

在 C++17 之前,我们必须在源文件中实例化类内静态变量。如今,你可以使用内联变量将声明和初始赋值合并到类定义中,如下所示。

struct BabaMrb {
static const int value = 10;
static inline std::string className = "Hello Class";
}

(4)其他特性

C++17 中还有许多我不知道如何归类的的其他特性。下面,我们来逐一介绍。

复制省略

复制省略(Copy elision),即返回值优化,是大多数编译器为防止在某些情况下出现额外副本而实现的优化。从 C++17 开始,直接返回对象时必然会触发复制省略。在某些情况下,即使只有一次复制操作也会影响系统的性能,例如对实时性有严格要求的系统。遇到这种情况,我们最好确保避免复制,以免降低系统性能。

struct C {
C() { std::cout << "Default constructor" << std::endl; }
C(const C&) { std::cout << "Copy constructor" << std::endl; }
};
C f() {
return C();  // Definitely performs copy elision
}
C g() {
C c;
return c;    // May perform copy elision
}
int main() {
C obj = f(); // Copy constructor isn't called
}

共享互斥锁

在使用共享互斥锁后,我们就可以按需读取对象而无需加锁,而写调用可以照往常一样使用常规互斥锁来锁定对象。共享互斥锁可以加快只读访问操作的速度,因为读取操作可以同步进行。

硬件干涉大小

这个新的库功能可以帮助你在编译期间确定 L1 缓存行的大小。有了这个功能,你就可以根据 L1 缓存行的大小调整结构、缓冲区等。我在使用 C++11 为 ARM Cortex-A9 内核实现低级裸机 DMA 驱动程序时就会用到这个功能,因为在编写这些代码时,我需要手动管理高速缓存和主内存之间的一致性。

尽管此功能非常强大,但直到版本 12 才在所有版本的 GCC 中实现,因此很可能你当前的编译器并不支持。如下代码是一个示例,可以帮助你更好地理解这个功能。

#ifdef __cpp_lib_hardware_interference_size // Undefined prior to C++17
using std::hardware_constructive_interference_size;
using std::hardware_destructive_interference_size;
#else
// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...
constexpr std::size_t hardware_constructive_interference_size   = 64;
constexpr std::size_t hardware_destructive_interference_size    = 64;
#endif
struct alignas(hardware_constructive_interference_size) OneCacheLiner { // occupies one cache line
std::atomic_uint64_t x{};
std::atomic_uint64_t y{};
};

总结

与 C++14 不同,C++17 引入了许多新特性。其中一些功能对嵌入式系统开发非常有帮助。

不同产品之间,嵌入式设备的计算能力差异很大。由于 CPU 性能、缺乏编译器支持、验证必要性等多种原因,我选择的某些功能可能不适用于你的固件。总体而言,迁移到 C++17 可能需要花费大量的时间和精力,请认真考虑是否需要迁移。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式系统
    +关注

    关注

    41

    文章

    3590

    浏览量

    129460
  • C++
    C++
    +关注

    关注

    22

    文章

    2108

    浏览量

    73636
  • 编译器
    +关注

    关注

    1

    文章

    1634

    浏览量

    49122

原文标题:从 C++11 升级至 C++17,它们让嵌入式系统更好了!

文章出处:【微信号:技术让梦想更伟大,微信公众号:技术让梦想更伟大】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    使用C++11新特性实现一个通用的线程池设计

    C++11标准之前,多线程编程只能使用pthread_xxx开头的一组POSIX标准的接口。C++11标准开始,多线程相关接口封装在了C++的std命名空间里。
    的头像 发表于 12-22 13:58 1290次阅读
    使用<b class='flag-5'>C++11</b>新特性实现一个通用的线程池设计

    嵌入式C++编程的相关资料分享

    特点展示如何使用 C ++ 来构建利用可用硬件资源的强大并发系统嵌入式编程入门和 C ++ 17
    发表于 11-09 08:26

    嵌入式系统中的Python与C / C ++的相关资料分享

    嵌入式python c++ 尽管C / C ++编程语言有很多缺点,但它们嵌入式
    发表于 12-15 07:14

    嵌入式系统C程序设计

    嵌入式系统C程序设计
    发表于 04-07 22:42 86次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>的<b class='flag-5'>C</b>程序设计

    由DP83846升级DP83848C/I/YB PHYTE

    由DP83846升级DP83848C/I/YB PHYTER系统的滚动支持文件
    发表于 02-14 17:29 45次下载

    嵌入式系统c程序设计

    嵌入式系统c程序设计
    发表于 02-11 09:21 65次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>的<b class='flag-5'>c</b>程序设计

    嵌入式C编程

    嵌入式C编程,非常有用的资料,介绍嵌入式C语言编程
    发表于 12-29 17:29 0次下载

    C嵌入式系统编程

    C嵌入式系统编程
    发表于 10-23 16:27 14次下载

    嵌入式系统C程序设计

    嵌入式系统C程序设计
    发表于 10-30 10:20 13次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>系统</b>与<b class='flag-5'>C</b>程序设计

    《深入理解C++11C++11新特性解析与应用的详细电子教材免费下载

    国内首本全面深入解读 C++11 新标准的专著,由 C++ 标准委员会代表和 IBM XL 编译器中国开发团队共同撰写。不仅详细阐述了 C++11 标准的设计原则,而且系统地讲解了
    发表于 08-27 08:00 0次下载

    C++17 STL标准库学习教材电子书免费下载

    C++11C++14和C++17标准为C++添加了许多新特性。当前的C++已经和10年前的C+
    发表于 02-28 08:00 7次下载
    <b class='flag-5'>C++17</b> STL标准库学习教材电子书免费下载

    嵌入式C++编程

    特点展示如何使用 C ++ 来构建利用可用硬件资源的强大并发系统嵌入式编程入门和 C ++ 17
    发表于 11-04 10:36 10次下载
    <b class='flag-5'>嵌入式</b><b class='flag-5'>C</b>++编程

    c++入门后如何进阶

    C++11 是下一个 C++ 标准,但我们通常称之为现代 C++。现代 C++ 也包括了 C++14 和
    发表于 07-21 08:56 343次阅读
    <b class='flag-5'>c</b>++入门后如何进阶

    C++98到C++26,经历了什么?

    上个月,C++26时间表发布,将会在“并发和并行性方面有重大改进”。而已经推出的C++ 23版本则沿袭了C++17的传统特征,完善了现有特性。但是,与C++ 98、
    的头像 发表于 08-09 16:01 1059次阅读
    <b class='flag-5'>从</b><b class='flag-5'>C</b>++98到<b class='flag-5'>C</b>++26,经历了什么?

    JDK11升级JDK17最全实践

    2021年9月14日,Oracle发布了可以长期支持的JDK17版本,那么JDK11到JDK17,到底带来了哪些特性呢?亚毫秒的ZGC效
    的头像 发表于 11-17 10:36 1515次阅读
    JDK<b class='flag-5'>11</b><b class='flag-5'>升级</b>JDK<b class='flag-5'>17</b>最全实践