网站建设和推广话术6,企业管理咨询服务合同,58同城最新招聘网,无锡网站建设电话constexpr
constexpr的隐含意思是在编译阶段求值#xff0c;对于一些求值操作#xff0c;如果声明为constexpr#xff0c;那么会编译器会尝试在编译阶段进行计算求值#xff0c;如果求值成功#xff0c;则用结果进行替换。
一个常用的例子是如下#xff1a;
constexpr…
constexpr
constexpr的隐含意思是在编译阶段求值对于一些求值操作如果声明为constexpr那么会编译器会尝试在编译阶段进行计算求值如果求值成功则用结果进行替换。
一个常用的例子是如下
constexpr int factorial(int n) {return n 1 ? 1 : (n * factorial(n - 1));
}int main() {auto num factorial(10); // 编译阶段求得值可从汇编查看return 0;
}
如果一个变量声明为constexpr那么意味着在编译阶段就要获得其值比如如下这个例子
#include iostreamconstexpr int factorial(int n) {return n 1 ? 1 : (n * factorial(n - 1));
}int main() {int i 10;constexpr auto num factorial(i);return 0;
}
因为num被声明为constexpr所以正如前面所说在编译阶段求值又因为i是一个非常量表达式所以编译器报错如下
source: In function int main():
source:10:35: error: the value of i is not usable in a constant expression10 | constexpr auto num factorial(i);| ^
source:9:8: note: int i is not const9 | int i 10;| ^
Compiler returned: 1
if语句初始化
这个是自C17才支持的可以在if语句中进行初始化随后进行条件判断如下
if (int a 0; a ! 10);
也可以像下面这样
std::vectorint v;
// operation
if (auto size v.size());
引用赋值不会改变其初始指向
示例
int x 2, y 3;
int r1 x;
int r2 y;
r1 r2;
std::cout x y r1 r2;
在上述代码中虽然最后r1被赋值为r2但是其仍然指向x这样的结果就是x的值也被修改了。最后输出结果为3 3 3 3
enum vs enum类
传统的enum如果像下面这样定义
enum Colors {Red,Green,Blue};enum OtherColors {Yellow,Blue};
编译器会报如下错误
source:16:5: error: Blue conflicts with a previous declaration16 | Blue| ^~~~
source:11:5: note: previous declaration Colors Blue11 | Blue| ^~~~
为了解决这个问题引入了enum class如下
enum class Colors {Red,Green,Blue};enum class OtherColors {Yellow,Blue};
modules
示例如下
// Vector.h:
class Vector {public:Vector(int s);double operator[](int i);int size();private:double∗ elem; // elem points to an array of sz doublesint sz;
};
// Vector.cpp:
#include Vector.h // get Vector’s interface
Vector::Vector(int s) :elem{new double[s]}, sz{s} {
}
double Vector::operator[](int i) {return elem[i];
}
int Vector::size() {return sz;
}
// user.cpp
#include Vector.h // get Vector’s interface
#include cmath // get the standard-librar y math function interface including sqrt()
double sqrt_sum(Vector v) {double sum 0;for (int i0; i!v.siz e(); i)sumstd::sqr t(v[i]); // sum of square rootsreturn sum;
}
最终格式如下 可以单独对user.cpp 和 Vector.cpp编译生成.o文件这是因为上述示例使用了#include操作预处理器在遇到#include的时候会将其中的内容完整的拷贝一份到相应的文件这就导致每个.cpp都有头文件Vector.h的一个副本代码体积膨胀不说还增加了编译时间。
为了解决上述问题引入了modules使用module优化上述代码如下
// Vector.cpp
module;
export module Vector; // defining the module called Vector
export class Vector {public:Vector(int s);double operator[](int i);int size();private:double∗ elem; // elem points to an array of sz doublesint sz;
};Vector::Vector(int s) :elem{new double[s]}, sz{s} {
}
double Vector::operator[](int i) {
return elem[i];
}
int Vector::siz e() {
return sz;
}
expor t int size(const Vector v) { return v.siz e(); }
// user.cpp
import Vector; // get Vector’s interface
#include cmath // get the standard-librar y math function interface including sqrt()
double sqrt_sum(Vector v) {double sum 0;for (int i0; i!v.siz e(); i)sumstd::sqr t(v[i]); // sum of square rootsreturn sum;
}
对于这块的内容可以详细阅读之前的文章:
未来已来C modules初探
纯虚函数
如果其中一个成员函数使用 0那么该函数为纯虚函数继承于存在纯虚函数的子类其必须实现该函数
class Base {public:void fun() 0;
};class Derived : public Base {public:void fun() {}
}
如果声明一个存在纯虚函数的类那边编译器会报错如下
source:21:8: error: initializer specified for non-virtual method void Base::fun()21 | void fun() 0;| ^~~
派生
判断一个类是否继承于另外一个类可以使用如下方式
templatetypename Base, typename T
inline bool IsDerivedOf(T *ptr) {return dynamic_castBase*(ptr) ! nullptr;
}
智能指针
智能指针可以避免内存泄漏此处文章较多可以参考
智能指针-使用、避坑和实现
一次诡异的内存泄漏
显式构造
对于如下这种
class Vector {public:Vector(int sz);
}
可以使用Vector v 3;这种方式进行初始化往往这种并不是我们希望看到的所以可以使用关键字explicit来强制显式初始化
class Vector {public:explicit Vector(int sz);
}
move语义
自C11起引入了move语义不过这个很容易引起初学者的误解其实move()本身并不做任何操作其只是进行了简单的类型转换而真正的移动操作需要类实现者进行定义。
STL对其定义如下
templatetypename _Tpconstexpr typename std::remove_reference_Tp::typemove(_Tp __t) noexcept{ return static_casttypename std::remove_reference_Tp::type(__t); }
如果需要深入了解其特性可以参考文章
【Modern C】深入理解移动语义
CTAD
CTAD为Class Template Argument Deduction的缩写中文称为类模板参数推导在C17之前我们需要像下面这样写
std::pairint, double p1 {1, 3.41};
现在我们可以如下这样写
std::pair p2 {1, 3.41};
可以参考文章【ModernCpp】新特性之CTAD
字面量
如果我们像下面这样写
auto s hello;
s会被编译器推导为const char*为了使得其为std::string类型有以下几种方式
auto s1 std::string(hello);
std::string s2 hello;
auto s3 hellos;
前两种方式比较常见第三种方式是Modern cpp的新特性俗称字面量。在这个语法中hello是字符串字面值而s是用户定义字面量的后缀它将字符串字面值转换为std::string对象。
COW VS SSO
COW想必大家都清楚其原理这个机制很常用比较常见的如fork等操作在STL中也有用到这个比如gcc5.1之前的string中先看如下代码
std::string s(str);std::string s1 s;char *p const_castchar*(s1.data());p[2] \0;std::cout s std::endl;std::cout s1 std::endl;
输出结果无非以下两种
st
st
或者
str
st
第一种基于gcc5.1前的版本编译第二种输出基于5.1之后的版本编译这两个输出的不同正是源于gcc5.1之前的版本对于string的复制采用了COW操作。
自gcc5.1之后字符串优化采用了新的机制即SSO其为Small String Optimization的简写中文译为小字符串优化基本原理是当分配大小小于16个字节时候从栈上进行分配而如果大于等于16个字节则在堆上进行内存分配