【C++】C++11新增关键字详解
目录
- 一、auto
- 1、auto 用来声明自动变量,表明变量存储在栈(C++11之前)
- 2、auto用于推断变量类型示例(C++11)
- 3、声明或定义函数时作为函数返回值的占位符,此时需要与关键字 decltype 一起使用。(C++11)
- 二、using
- 1、using 在 C++11之前主要用于名字空间、类型、函数与对象的引入,实际上是去除作用域的限制。
- 2、通过using引入函数可以解除函数隐藏
- 3、使用 using 代替 typedef,给类型命名
- 三、decltype
- 1)推导出表达式类型。
- 2)与using/typedef合用,用于定义类型。
- 3)重用匿名类型。在 C++ 中,我们有时候会遇上一些匿名类型。
- 4)泛型编程中结合 auto,用于追踪函数的返回值类型,这是 decltype的最大用途。decltype 帮助 C++ 模板更加泛化,程序员在编写代码时无需关心任何时段的类型选择,编译器会合理地进行推导。
- 四、nullptr_t 与 nullptr
- 五、constexpr
- 1、简介
- 2、constexpr 的应用
- 1)常量表达式函数
- 2)常量表达式值
- 3)常量表达式的其他应用
- 3、constexpr 与 const 的区别
- 六、noexcept
- 1、修饰符示例。
- 2、操作符示例。
- 七、final
- 1、final用于修饰类。
- 2、final用于修饰虚函数。
- 八、override
- 九、sizeof… 运算符
- 十、default
- 十一、delete
- 1、禁止编译器生成上面六种函数的默认版本。
- 2、C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数。在函数重载中,可用delete来滤掉一些函数的形参类型,如下:
- 3、在模板特例化中,也可以用 delete 来过滤一些特定的形参类型。
- 十二、static_assert
- 十三、alignas 与 alignof
- 十四、thread_local
- 参考博客
一、auto
1、auto 用来声明自动变量,表明变量存储在栈(C++11之前)
2、auto用于推断变量类型示例(C++11)
auto i = 42; //i is an int
auto l = 42LL; //l is an long long
auto p = new foo(); //p is a foo*
3、声明或定义函数时作为函数返回值的占位符,此时需要与关键字 decltype 一起使用。(C++11)
auto不能用来声明函数的返回值。但如果函数有一个尾随的返回类型时,auto是可以出现在函数声明中返回值位置。这种情况下,auto并不是告诉编译器去推断返回类型,而是指引编译器去函数的末端寻找返回值类型。
在下面这个例子中,函数返回值类型是operator+操作符作用在T、U类型变量上的返回值类型。
template<class T, class U> auto add(T t, U u) -> decltype(t + u)
{return t + u;
}
二、using
1、using 在 C++11之前主要用于名字空间、类型、函数与对象的引入,实际上是去除作用域的限制。
//引入名字空间using namespace std;//引入类型using std::iostream;//引入函数using std::to_string;//引入对象using std::cout;
2、通过using引入函数可以解除函数隐藏
“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)
2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)
使用了using关键字,就可以避免1的情况,是的父类同名函数在子类中得以重载,不被隐藏
class Base{public:void func() { cout << "in Base::func()" << endl; }void func(int n) { cout << "in Base::func(int)" << endl;}};class Sub : public Base {public:using Base::func; //引入父类所有同名函数func,解除函数隐藏void func() { cout<<"in Sub::func()"<<endl;}};int main() {Sub s;s.func();s.func(1); // Success!}
3、使用 using 代替 typedef,给类型命名
using uint8=unsigned char; //等价于typedef unsigned char uint8;
using FunctionPtr = void (*)(); //等价于typedef void (FunctionPtr)();
template using MapString = std::map<T, char>; //定义模板别名,注意typedef无法定义模板别名,因为typedef只能作用于具体类型而非模板
三、decltype
随着 C++ 模板和泛型编程的广泛使用,类型推导成为了 C++ 必备的一个能力。在 decltype 出现之前,很多编译器厂商都实现了自己的 C++ 扩展特性用于类型推导,比如 GCC 的 typeof 操作符。
C++11 将这些类型推导手段进行了细致的考量,最终标准化为 auto 与 decltype。decltype 与auto 关键字类似,用于编译时类型推导,不过它与 auto 还是有一些区别的。
decltype 的类型推导并不像 auto 从变量声明的初始化表达式获得变量的类型,而总是以一个普通表达式作为参数,返回该表达式的类型,而且 decltype 并不会对表达式进行求值。
decltype 推导规则
(1)如果 e 是一个变量或者类成员访问表达式,假设e的类型是T,那么的decltype(e)为T,decltype((e))为T&。
(2)如果e是一个解引用操作,那么decltype(e)和decltype((e))均为T&。
(3)否则decltype(e)与decltype((e))均为T。
用法示例
1)推导出表达式类型。
struct A { double x; };
const A* a = new A{0};
//第一种情况
decltype(a->x) y; // type of y is double
decltype((a->x)) z = y; // type of z is const double&,因为a一个常量对象指针
//第二种情况
int* aa=new int;
decltype(*aa) y=*aa; //type of y is int&,解引用操作
//第三种情况
decltype(5) y; //type of y is int
decltype((5)) y; //type of y is int
const int&& RvalRef() { return 1; }
decltype ((RvalRef())) var = 1; //type of var is const int&&
}
2)与using/typedef合用,用于定义类型。
using size_t = decltype(sizeof(0));//sizeof(a)的返回值为size_t类型
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);vector<int>vec;
typedef decltype(vec.begin()) vectype;
for (vectype i = vec.begin; i != vec.end(); i++){...}
显而易见,与auto一样,也提高了代码的可读性。
3)重用匿名类型。在 C++ 中,我们有时候会遇上一些匿名类型。
struct {int d ;doubel b;}anon_s;
借助 decltype,我们可以重新使用这个匿名的结构体,C++11 之前我们是无法做到的。
decltype(anon_s) as ; //定义了一个上面匿名的结构体
注意,匿名类型有其匿名的原因,一般情况下,匿名类型不应该被重用,应尽量避免这种用法。
4)泛型编程中结合 auto,用于追踪函数的返回值类型,这是 decltype的最大用途。decltype 帮助 C++ 模板更加泛化,程序员在编写代码时无需关心任何时段的类型选择,编译器会合理地进行推导。
template <typename _Tx, typename _Ty> auto multiply(_Tx x, _Ty y)->decltype(x*y) { return x*y; }
四、nullptr_t 与 nullptr
C++11 之前都是用 0 来表示空指针,但由于 0 可以被隐式类型转换为整型,这就会存在一些问题。关键字 nullptr 是 std::nullptr_t 类型的值,用来指代空指针常量。
nullptr 和任何指针类型以及类成员指针类型的空值之间可以发生隐式类型转换,同样也可以隐式转换为 bool 型(取值为false),但是不存在到整型的隐式转换。
int* p1 = NULL;//或int* p2 = nullptr;
在使用 nullptr_t 与 nullptr 时,注意以下几点:
(1)可以使用nullptr_t定义空指针,但所有定义为nullptr_t类型的对象行为上是完全一致的;
(2)nullptr_t 类型对象可以隐式转换为任意一个指针类型;
(3)nullptr_t 类型对象不能转换为非指针类型,即使使用reinterpret_cast进行强制类型转换也不行;
(4)nullptr_t 类型对象不能用于算术运算表达式;
(5)nullptr_t 类型对象可以用于关系运算表达式,但仅能与 nullptr_t 类型或指针类型对象进行比较,当且仅当关系运算符为==、>=、<=时,如果相等则返回 true。
五、constexpr
1、简介
constexpr 在 C++11 中用于申明常量表达式 (const expression),可作用于函数返回值、函数参数、数据申明以及类的构造函数等。
常量表达式 指值不会改变并且在编译时期就得到计算结果的表达式,例如:
const int i=3; //i是一个常变量const int j=i+1; //j是一个常变量,i+1是一个常量表达式int k=23; //k的值可以改变,从而不是一个常变量const int m=f(); //m不是常变量,m的值只有在运行时才会获取
2、constexpr 的应用
1)常量表达式函数
如果函数返回值在编译时期可以确定,那么可以使用constexpr修饰函数返回值,使函数成为常量表达式函数。
constexpr int f(){return 1;}
注意,constexpr修饰函数返回值需要满足如下条件:
(a)函数必须有返回值;
(b)函数体只有单一的return语句;
(c)return语句中的表达式也必须是一个常量表达式;
(d)函数在使用前必须已有定义。
}
2)常量表达式值
一般来说,如果认定变量是一个常量表达式,那就把它声明为constexpr类型。
constexpr int i=3; //i是一个常变量
constexpr int j=i+1; //i+1是一个常变量
constexpr int k=f(); //只有f()是一个constexpr函数时,k才是一个常量表达式
必须明确一点,在constexpr声明中,如果定义了一个指针,constexpr仅对指针有效,与指针所指对象无关。
const int *p=nullptr; //p是一个指向整型常量的指针(pointer to const)
constexpr int *p1=nullptr; //p1是一个常量指针(const pointer)
如果自定义类型对象为常量表达式,那么在定义自定义类型时,需要将constexpr作用于自定义类型的构造函数。
struct MyType {int i;constexpr MyType(int x):i(x){}};constexpr MyType myType(1);
constexpr作用于自定义类型的构造函数需要满足如下条件:
(a)构造函数体必须为空;
(b)初始化列表只能使用常量表达式。
3)常量表达式的其他应用
(a)常量表达式作用于函数模板
常量表达式可以作用于函数模板,但是由于函数模板参数的不确定性,实例化后的模板函数可能不满足常量表达式的条件,此时,C++11标准规定,自动忽略constexpr。
struct NotConstType {int i;NotConstType(int x) :i(x) {}};NotConstType myType;//constexpr作用于函数模板template <typename T> constexpr T ConstExpFunc(T t) {return t;}int main(){NotConstType objTmp = ConstExpFunc(myType); //编译通过,ConstExpFunc实例化为普通函数,constexpr被忽略constexpr NotConstType objTmp1 = ConstExpFunc(myType); //编译**失败**constexpr int a = ConstExpFunc(1); //编译通过,ConstExpFunc实例化为常量表达式函数}
(b)constexpr元编程
constexpr可以作用于递归函数来实现编译时期的数值计算,即constexpr元编程。C++11标准规定,常量表达式应至少支持512层递归。
constexpr int Fibonacci(int n){return (n == 1) ? 1 : (n == 2 ? 1 : Fibonacci(n - 1) + Fibonacci(n - 2));}int main(){constexpr int fib8 = Fibonacci(8); //编译期常量等于21}
注意,constexpr元编程并非C++11标准强制规定编译器必须实现,编译器可以选择地实现。也就是说,编译器可能并不支持递归常量表达式函数。不过也不用过于担心,主流的C++编译器都是支持的,比如GCC和VC++。
3、constexpr 与 const 的区别
const 可以修饰函数参数、函数返回值、函数本身、类等,在不同的使用场景下,const具有不同的意义,不过大多数情况下,const描述的是“运行时常量性”,即在运行时数据具有不可更改性。
constexpr可以修饰函数参数、函数返回值、变量、类的构造函数、函数模板等,是一种比const更加严格的约束,它修饰的表达式除了具有“运行时常量性”,也具有“编译时常量性”,即constexpr修饰的表达式的值在编译期间可知。下面看一个实际应用时的区别:
const int getConst(){ return 1; }enum{ e1=getConst(),e2}; //编译出错//换成constexpr即可在编译期确定函数返回值用于初始化enum常量constexpr int getConst(){ return 1; }enum{ e1=getConst(),e2}; //编译OK
在constexpr出现之前,可以在编译期初始化的const表达式都是隐含的常量表达式(implicit constexpr),直到C++ 11,constexpr才从const中细分出来成为一个关键字,
而 const从1983年C++刚改名的时候就存在了。面对constexpr,我们应当尽可能地、合理地使用constexpr来帮助编译器优化代码。
六、noexcept
在 C++11 标准之前,C++在函数声明中有exception specification(异常声明)的功能,用来指定函数可能抛出的异常类型。
voidFunc0() throw(runtime_error);// 函数 Func0 可能抛出 runtime_error 类型的异常;
voidFunc1() throw();// 函数 Func1 不会抛出任何异常;
voidFunc2();// 函数 Func2 没有异常说明,则该函数可以抛出任何类型的异常。
如果函数抛出了没有在异常说明中列出的异常,则编译器会调用标准库函数unexpected。默认情况下,unexpected函数会调用terminate函数终止程序。
这种异常声明的功能很少使用,因此在 C++11 中被弃用(实际仍可使用)。C++11 引入 noexcept,具有两层含义,一个是修饰符,二是操作符。具体用法如下。
1、修饰符示例。
voidFunc3() noexcept;
noexcept的功能相当于上面的throw(),表示函数不会抛出异常。如果noexcept修饰的函数抛出了异常,编译器可以选择直接调用std::terminate()终止程序运行。noexcept比throw()效率高一些。
voidFunc4() noexcept(常量表达式);
如果常量表达式的结果为true,表示该函数不会抛出异常,反之则有可能抛出异常。不带常量表达式的noexcept相当于noexcept(true)。
2、操作符示例。
上面 noexcept 的用法是其作为修饰符时的用法,实际上 noexcept 还可以作为操作符,常用于模板中。
template <typename T> void func5() noexcept(noexcept(T())) {}
第二个 noexcept 是一个操作符,如果其参数是一个有可能抛出异常的表达式,noexcept(T()) 则返回值为false,那么 func5 有可能会抛出异常,否则 noexcept(T()) 返回true,func5 不会抛出异常。这样函数模板是否会抛出异常,可以由表达式进行推导,使得 C++11 更好的支持泛型编程。参考以下示例:
template <typename T>
void fun() noexcept(noexcept(T())) { throw 1; }class Base{
public:virtual void f() {}
};
class Test :public Base{
public:~Test() noexcept(true) {}
};
class TestFalse :public Base{
public:~TestFalse() noexcept(false) {}
};int main(int argc, char **argv){std::cout << noexcept(TestFalse()) << std::endl; // falsestd::cout << noexcept(Test()) << std::endl; // truetry {fun<TestFalse>();} catch (...) {std::cout << "throw" << std::endl; // throw}try {fun<Test>(); // terminate} catch (...) {std::cout << "throw" << std::endl;}return 0;
}
程序输出:
throw
terminate called after throwing an instance of 'int'
Aborted
七、final
1、final用于修饰类。
final修饰类,可用于声明终结类。
struct B1 final {};
struct D1 : B1 {}; //错误!不能从final类继承!
上面的代码是错误的,因为 D1 试图继承 B1,而 B1 被 final声明为终结类,类似于Java的关键字的作用。
2、final用于修饰虚函数。
final用于修饰虚函数,表明子类不能重写该虚函数,为”终结虚函数“。例如:
struct B2{virtual void f() final {} // final 函数};struct D2 : B2{virtual void f() {}};
这段代码会出错,因为D2::f重写了B2::f,但是B2::f却被声明为 final 。
值得注意的是,这些并不是一些奇技淫巧,而是能确确实实地避免很多程序错误,并且暗示编译器作出一些优化。调用标记了 final 的 virtual 函数,
例如上面的B2::f,GNU C++ 编译时会识别出来这个函数不能被重写,因此会将其从类的虚表中删除。
而标记为final的类,例如上面的 B1,编译器则根本不会生成虚表,这样的代码显然效率更高。
八、override
假如我们继承基类的虚函数,在重写虚函数时写错了,参数类型不对或个数不对,但是编译没问题,造成了对基类同名函数的隐藏,运行时候和设计的不一样,
override就是辅助检查是否正真重写了继承的虚函数。例如:
struct B3{virtual void f() {}};struct D3 : B3{void f(int a) {} //未重写,发生隐藏,但不会报编译错误};
开发 D3 的程序员真的想重写B3::f函数吗?还是说,他只是不小心写了个与父类同名的函数,却在不经意间导致了隐藏?
为了避免这种错误,C++11引入了override关键字。于是,我们会发现,下面这段代码会出错:
struct B4{virtual void g(int) {}};struct D4 : B4{virtual void g(int) override {} // OKvirtual void g(double) override {} // Error};
多亏了override关键字,我们可以让编译器帮我们检测到这个很难发现的程序错误。
这段代码的错误在于,override关键字表明,g(double)虽然想要进行override的操作,但父类并没有这么个函数。
在实际开发中,建议大家重写继承而来的虚函数时,加上关键字 virtual 表明当前函数是虚函数,C++编译器的“放纵”降低了代码可读性。
九、sizeof… 运算符
sizeof…运算符(后面有三个点)的作用是获取C++11中可变参数模板中参数包中元素个数。类似sizeof,sizeof…返回一个常量表达式,而且不会对模板的实参求值。例如:
template<typename... Args> void g(Args... args){cout<<sizeof...(Args)<<endl; //类型参数的数目cout<<sizeof...(args)<<endl; //函数参数的数目
十、default
我们知道,C++98和C++03编译器在类中会隐式地产生四个函数:默认构造函数、拷贝构造函数、析构函数和赋值运算符函数,它们被称为特殊成员函数。
在 C++11 中,被称为 “特殊成员函数” 的还有两个:移动构造函数 和 移动赋值运算符函数。如果用户申明了上面六种函数,编译器则不会隐式产生。
C++引入的default关键字,可显示地、强制地要求编译器为我们生成默认版本。
class DataOnly{public:DataOnly()=default; //default constructor~DataOnly()=default; //destructorDataOnly(const DataOnly& rhs)=default; //copy constructorDataOnly& operator=(const DataOnly & rhs)=default; //copy assignment operatorDataOnly(const DataOnly && rhs)=default; //C++11,move constructorDataOnly& operator=(DataOnly && rhs)=default; //C++11,move assignment operator};
上面的代码,就可以让编译器生成上面六个函数的默认版本。
十一、delete
delete关键在C++11之前是对象释放运算符,但在C++11中,被赋予了新的功能,主要有如下几种作用。
1、禁止编译器生成上面六种函数的默认版本。
class DataOnly{public:DataOnly()=delete; //default constructor~DataOnly()=delete; //destructorDataOnly(const DataOnly& rhs)=delete; //copy constructorDataOnly& operator=(const DataOnly & rhs)=delete; //copy assignment operatorDataOnly(const DataOnly && rhs)=delete; //C++11,move constructorDataOnly& operator=(DataOnly && rhs)=delete; //C++11,move assignment operator};
2、C++11 中,delete 关键字可用于任何函数,不仅仅局限于类成员函数。在函数重载中,可用delete来滤掉一些函数的形参类型,如下:
bool isLucky(int number); // original functionbool isLucky(char) = delete; // reject charsbool isLucky(bool) = delete; // reject boolsbool isLucky(double) = delete; // reject doubles and floats
这样在调用 isLucky 函数时,如果参数类型不对,则会出现错误提示
if (isLucky('a'))... // error! call to deleted functionif (isLucky(true))... // error!if (isLucky(3.5))... // error!
3、在模板特例化中,也可以用 delete 来过滤一些特定的形参类型。
例如,Widget 类中声明了一个函数模板,当进行模板特化时,要求禁止参数为 void* 的函数调用。
class Widget{public:template<typename T> void processPointer(T* ptr){}};template<> void Widget::processPointer<void>(void*)=delete; //deleted function template
十二、static_assert
static_assert是C++11引入的静态断言,与assert(运行时断言宏)相反,用于检测和诊断编译期错误。基本语法如下:
static_assert(断言表达式,提示字符串);
断言表达式必须是在编译期可以计算的表达式,即必须是常量表达式。
如果断言表达式的值为 false ,那么编译器会出现一个包含指定字符串的错误,同时编译失败。
如果断言表达式的值为 true ,那么没有任何影响。例如:
static_assert(sizeof(void*) == 8,"not supported");
static_assert 和 type traits 一起使用能发挥更大的威力。
type traits 是一些类模板,在编译时提供关于类型的信息,在头文件<type_traits>中可以找到它们。
这个头文件中有好几种类模板:
有 helper class, 用来产生编译时常量,
有 type traits class,用来在编译时获取类型信息,
有 type transformation class,他们可以将已存在的类型变换为新的类型。
下面这段代码原本期望只作用于整数类型。
template <typename T1, typename T2> auto add(T1 t1, T2 t2){return t1 + t2;}
但是如果有人写出如下代码,编译器并不会报错。
std::cout << add(1, 3.14) << std::endl;std::cout << add("one", 2) << std::endl;
程序会打印出4.14和”e”。但是如果我们加上编译时断言,那么以上两行将产生编译错误。
template <typename T1, typename T2> auto add(T1 t1, T2 t2){static_assert(std::is_integral<T1>::value, "Type T1 must be integral");static_assert(std::is_integral<T2>::value, "Type T2 must be integral");return t1 + t2;}
使用 static_assert,应当注意:
(1)static_assert可以用在全局作用域,命名空间,类作用域,函数作用域,几乎可以不受限制地使用;
(2)static_assert可以在帮助我们在编译期间发现更多的错误,用编译器来强制保证一些契约,改善编译信息的可读性,尤其是用于模板的时候;
(3)编译器在遇到一个static_assert语句时,通常立刻将其第一个参数作为常量表达式进行演算。如果第一个常量表达式依赖于某些模板参数,则延迟到模板实例化时再进行演算,这就让检查模板参数成为了可能;
(4)由于是static_assert编译期间断言,不生成目标代码,因此static_assert不会造成任何运行期性能损失。
十三、alignas 与 alignof
内存对齐 指变量起始存储地址和类型大小是对齐字节数的整数倍。
例如某个int型变量,其起始存储地址0x0000CC04是4的整数倍,那么这个变量就是对齐的。
在C++11之前对齐方式是无法得知的,且不同的平台实现方式可能不同。
C++11为了支持内存对齐,引入了两个关键字,对齐描述符 alignas 与操作符 alignof 。
alignas 用于指定类型的对齐字节数,alignof 用于获取类型的对齐字节数。
alignas 不仅可以作用于类型,也可以作用于成员变量。并且 alignas 既可以接受常量表达式,也可以接受类型作为参数。
所以alignas(char) int i等价于alignas(alignof(char)) int i。
使用常量表达式作为alignas的操作数时,其值必须是2的自然数次幂。对齐值越大,对齐要求越高;对齐值越小,对齐要求越低。显然,能够满足严格对齐要求的对齐方式也能够满足要求低的对齐方式。
alignof的操作数表示一个定义完整的自定义类型或者内置类型或者变量,返回一个std::size_t类型的整型常量。如同sizeof操作符一样,alignof获得的也是一个与平台相关的值。具体用法参考如下代码:
struct Example1{char c;int i;long long l;};struct alignas(16) Example2{char c;alignas(8) int i; //alignas作用于成员变量long long l;};Example2 array[1024];int main(){std::cout << alignof(Example1) << std::endl; // 8std::cout << sizeof(Example1) << std::endl; // 16std::cout << alignof(Example2) << std::endl; // 16std::cout << sizeof(Example2) << std::endl; // 32std::cout << alignof(int) << std::endl; // 4alignas(char) int i; std::cout << alignof(i) << std::endl; // 1std::cout<<alignof(array)<<std::endl; // 16return 0;}
//编译选项:g++ -std=c++11
理解上述程序输出结果,需要注意以下几点:
1)构造类型的对齐取决于类型对齐值和各成员对齐值中最大的那个,所以
sizeof(Example2)=sizeof(char)+7+sizeof(int)+4+sizeof(long long)+8=1+7+4+4+8+8=32
2)数组的对齐值由其元素决定,所以alignof(array)等于alignof(Example2),等于16。
此外,对内存对齐的支持,C++11在标准库中还提供了std::align()函数来动态地根据指定的对齐方式调整数据块的位置。以及在STL中提供的类模板aligned_storage及aligned_union,其作用与std::align()函数类似,具体用法不详细展开,感兴趣的读者可自行了解。
十四、thread_local
thread_local由C++11引入,用于将全局或static变量声明为线程局部存储(TLS,thread local storage)变量,即拥有线程生命周期及线程可见性的变量。
比如程序中有一个全局变量errCode,开启多个线程,每个线程都需要使用这个全局的errCode变量,不同的线程设置不同的错误码,
但是,又不能让所有线程同时访问同一个errCode,不然无法正确获取每个线程的错误码。此时有两个解决方案:
1)改变定义errCode的位置,把errCode从全局变量变为线程的局部变量(比如一个函数中)。
2)定义errCode的位置不变,依然是全局,只要加一个 thread_local 关键词,其他什么都不用改。
thread_local int errCode;
一旦申明一个变量为thread_local,其值的改变只对所在线程有效,其它线程不可见。
参考博客
C++11 新关键字:
相关文章:

linux批量创建用户和密码
老男孩教育第五关实战考试题:批量创建10个用户stu01-stu10,并且设置随机8位密码,要求不能用shell的循环(例如:for,while等),只能用linux命令及管道实现。 方法1:[rootoldboy /]# ech…

“重构”黑洞:26岁MIT研究生的新算法 | 人物志
点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」全日程揭晓,请扫码咨询 ↑↑↑整理 | 若名出品 | AI科技大本营(ID:rgznai100)这是一个重要时刻。除了发布跟丈夫的两张合照外,Katie Bouman 在 Facebook 上鲜有内容更新&#…

【Ubuntu】VirtualBox显卡驱动VBoxVGA、VBoxSVGA、VMSVGA +3D对播放视频的影响
一、VBOXVGA、VMSVGA、VBOXSVGA简述 VBOXVGA和VBOXSVGA是vbox自己的,SVGA比VGA先进一点, VBoxSVGA: 使用Linux或者 Windows 7或者更高版本的新vm的默认图形控制器。 与传统的VBoxVGA选项相比,此图形控制器可提高性能和3D支持。 VBoxVGA: 将这…

MFC中利用CFileDialog选择文件并读取文件所遇到的问题和解决方法
在用MFC编写一个上位机时,需要实现选择和读取一个二进制文件,本来以为很简单的但是在实现过程中遇到很多问题,所幸都一一解决,这里做一下记录。 首先在实现文件选择,在界面上设置一个按钮,并在点击事件函数…

百度智能云一口气发布 14 个新产品,三大视频解决方案,产品最高降价 50%
产业智能化的浪潮正在加速传统互联网行业的升级,视频行业将成为最大的受益者。4 月 11 日,在 2019ABC INSPIRE 百度云智峰会上,百度副总裁、百度智能云总经理尹世明宣布,“百度云” 品牌全面升级为 “百度智能云”,以 …

开源代码hosting openfoundryfrom tw
http://www.openfoundry.org

倒计时1天!「2019 Python开发者日」报名即将关闭(附参会提醒)
「2019 Python开发者日」倒计时最后1天,仅剩少量余票,请扫码咨询 ↑↑↑相信很多人听过之前的 Python 进入小学课本、Python 进入浙江省高考等新闻,那么,有这么多头衔加持的 Python 究竟魅力在哪?与人工智能、大数据捆…

【Gstreamer】在虚拟机中无法使用硬件加速:gstreamer1.0-vaapi
1、问题描述 在虚拟机中,使用gstreamer播放视频,在没有安装gstreamer1.0-vaapi库时,还是正常的;在安装gstreamer1.0-vaapi后,不能播放视频。 错误信息如下: libva info: VA-API version 0.39.0 libva info: va_getDriverName() returns -1 libva error: va_getDriverNa…

如何在阿里云上安全的存放您的配置 - 续
在《如何在阿里云上安全的存放您的配置》一文中,我们介绍了如何通过ACM存放您的敏感配置,并进行加密。这样做的目的有两个: 在应用程序或对应生产环境容器或系统中,无需持久化任何敏感数据信息(如数据库连接串,等)&…

VLAN-VTP-Trunk
VLAN(Virtual LAN) VLAN可以隔离2层的广播域。A VLAN =(一个) 广播域 = (一个)逻辑子网路由器是隔离广播域的单个端口只能承载单个VLAN的流量。使用VLAN好处:1.有效的带宽利用2.提高了安全性3…

科大讯飞刷新纪录,机器阅读理解如何超越人类平均水平? | 技术头条
点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」明日开启,扫码咨询 ↑↑↑记者 | 琥珀出品 | AI科技大本营(公众号ID:rgznai100)对于日常从事模型训练的研究人员来讲,无论是图像处理还是语音识别,都离…

【经验】Lenovo/ThinkPad 进入BIOS的方法汇总
1、快捷汇总 联想电脑进入BIOS的快捷键有“F2、F1、Del/Delete、NOVO开机”,部分机型按F2、F1时需要FN键配合 2、常用键 Lenovo笔记本:F2 Fn Lenovo台式机:F2 ThinkPad:F1 联系官网说明: http://tsonline.lenovo.…

NO.7 今天我们是实用派,看看业务选择和部署以及常用故障解决方案是怎么做的...
Hello,大家好,这是第七期 上云用户必看期刊,本期我们主打实用派的相关业务选择和部署以及一些常用故障解决方案做分享。 今天我们不平凡,成为实用派 运维工程师需要掌握的技能https://yq.aliyun.com/articles/591171?spma2c4e.11…

php基础知识
一:php变量#一、PHP的变量定义:变量用于存储值,比如数字、文本字符串或数组、五中:string /integer /double /array /object 命名规则:1、PHP的变量名是区分大小写的。 2、变量名必须以$开头 3、变量名开头可以是下划线 4、变量名…

【Qt】QPixmap加载图片报错:Corrupt JPEG data: premature end of data segment Didn‘t expect more than one scan
1、问题描述 在使用QPixmap加载图片时失败,错误信息如下 Corrupt JPEG data: premature end of data segment Didnt expect more than one scan使用QPicture加载时错误信息如下: QPicturePaintEngine::checkFormat: Incorrect header QPicturePaintEng…

Python超越Java,Rust持续称王!Stack Overflow 2019开发者报告
点击上方↑↑↑蓝字关注我们~「2019 Python开发者日」明日开启,扫码咨询 ↑↑↑作者 | 郭芮出品 | CSDN(ID:CSDNnews)导语:2019 年 Stack Overflow 开发者调查报告最新出炉了!今年,近 90,000 名…
electron打包可选择安装位置,可自动更新
Electron打包调参软件(windows版) ----------------------------------可选安装位置,可自动更新,手动更新 一:引包:electron,electron-builder,electron-updater** npm i electron --save-dev n…

osi 模型 tcpip网络模型
OSI网络分层参考模型 网络协议设计者不应当设计一个单一、巨大的协议来为所有形式的通信规定完整的细节,而应把通信问题划分成多个 小问题,然后为每一个小问题设计一个单独的协议。这样做使得每个协议的设计、分析、时限和测试比较容易。协议划分的一…

我在旷视研究院做检测 | 技术头条
作者 | 俞刚,旷视研究院Detection组负责人。2014年博士毕业于新加坡南洋理工大学,加入旷视。主要负责检测,分割,跟踪,骨架,动作行为等方面的研究以及算法落地工作。俞刚博士带队参加 2017 COCOPlaces 挑战赛…

【Ubuntu】ubuntu设置GUI程序自启动
1、在启动脚本中添加 在脚本中添加,如“/etc/rc/”“etc/rc.d”“/etc/rc?.d”“/ect/profile”“.bash_profile”等等。 百度下有很多讲解,这里不再赘述。 2、利用ubuntu界面系统启动 终端中执行:gnome-session-properties,出…

(转)关于数据库主键和外键(终于弄懂啦)
一、什么是主键、外键: 关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键 比如 学生表(学号,姓名,性别,班级) 其中每个学生的学号是唯…

浏览器兼容:IE6,IE7,IE8,FIREFOX,Chrome
javascript部分1. document.form.item 问题问题:代码中存在 document.formName.item("itemName") 这样的语句,不能在FF下运行解决方法:改用 document.formName.elements["elementName"]2. 集合类对象问题问题:…

如何将DynamoDB的数据增量迁移到表格存储
为什么80%的码农都做不了架构师?>>> 摘要: AWS 的 Amazon DynamoDB 和阿里云的表格存储 TableStore 都是完全托管的NoSQL数据库服务,提供快速的、可预期的性能,并且可以实现无缝扩展。本篇文章介绍了如何使用 Lambda …

【Ubuntu】ping: unknown host www.baidu.com
1、问题描述 每次重新设置网络后,ping百度总是报错: $ ping www.baidu.com ping: unknown host www.baidu.com2、原因分析 原因是:查看/etc/resolv.conf,发现没有设置DNS服务。 $ cat /etc/resolv.conf # Dynamic resolv.con…

马云:“996 是一种巨大的福气”
作者 | 伍杏玲出品 | CSDN(ID:CSDNnews)【导语】自3月27日996.ICU话题诞生以来,目前GitHub已获得21万的Star,引发国内外的广泛关注和热议。很多人质疑996工作制,Python之父Guido Van Rossum更直言“996反人…

Firebug Console 与命令行全集
Console API 当打开 firebug (也包括 Chrome 等浏览器的自带调试工具),window 下面会注册一个叫做 console 的对象,它提供多种方法向控制台输出信息,供开发人员调试使用。下面是这些方法的一个简单介绍,适时地运用它们,…

100%的程序员都想挑战的算法趣题!| 码书
计算机的世界每天都在发生着深刻的变化。新操作系统的发布、CPU性能的提升、智能手机和平板电脑的流行、存储介质的变化、云的普及……这样的变化数不胜数。在这样日新月异的时代中,“算法”是不变的重要基石。要编写高效率的程序,就需要优化算法。无论开…
【Qt】qss样式表之:QCalendarWidget,日历窗口样式表设置
1、效果图: 2、qss样式表 其中表头的背景颜色等设置不起作用,只好在下面的代码中实现。 /*日历*/ QCalendarWidget QHeaderView {qproperty-minimumSectionSize:0; } QCalendarWidget QMenu{background-color: rgb

BZOJ5324 洛谷4563 LOJ2545:[JXOI2018]守卫——题解
https://www.lydsy.com/JudgeOnline/problem.php?id5324 https://www.luogu.org/problemnew/show/P4563 https://loj.ac/problem/2545 题目见上。 参考:https://blog.csdn.net/dofypxy/article/details/80196942 区间dp,设f[i][j]为[i,j]的答案…

构建高可靠性网络
拓补图如下: 1. 浮动静态路由配置一条主链路,一条辅助链路!正常情况使用主链路,主链路出现故障,切换到辅助链路!H3C主线路 s0-s0 采用ospf 默认度量值是10,辅助线路 s1-s1 配置静态路由,默认度量值是10,无需调整,数据包默认值走s0-s0链路CISCO主线路 s0-s0 采用ospf 默认度量…