C++11中子类可以通过using声明来声明继承基类的构造函数。
struct A {
A(int i) {}
A(double d, int i) {}
A(float f, int i, const char* c) {}
};
struct B: A {
using A::A; //继承构造函数
//等价于
//B(int i): A(i) {}
//B(double d, int i): A(d, i) {}
//B(float f, int i, const char* c): A(f, i, c) {}
};
struct A1 {A1(int) {} };
struct B1 {B1(int) {} };
struct C: A1, B1 {
using A1::A1;
using B1::B1;
C(int) {} //需要显示声明继承类中的冲突的构造函数,阻止隐式生成相应的继承构造函数来解决冲突。
};
注意:
委派构造函数:在初始化列表中调用“基准版本”的构造函数为委派函数
目标构造函数:被调用的“基准版本”则为目标构造函数
注意:不能在初始化列表中既初始化列表中既初始化成员,又委托其构造函数完成构造。
注意:不能形成委托环。
class HasPtrMem {
public:
HasPtrMem(HasPtrMem &&h): d(h.d) { //移动构造函数
h.d = nullptr;
}
int *d;
};
可以取地址的、有名字的就是左值,反之,不能取地址的,没名字的就是右值。C++11中,右值由两个概念构成,一个是将亡值(右值引用相关的表达式,如右值引用T&&的函数返回值,std::move的返回值),另一个是纯右值。
注意:右值引用主要为了移动语义,而移动语义需要右值是可以被修改的,所以常量右值引用在移动语义中就没有用武之地。
C++11中引用类型及其可以引用的值类型如下表:
引用类型 | 非常量左值 | 常量左值 | 非常量右值 | 常量右值 | 注记 |
---|---|---|---|---|---|
非常量左值引用 | Y | N | N | N | 无 |
常量左值引用 | Y | Y | Y | Y | 全能类型,可用于拷贝语义 |
非常量右值引用 | N | N | Y | N | 用于移动语义,完美转发 |
常量右值引用 | N | N | Y | Y | 暂无用处 |
//std::move等同与下面转化,唯一的功能是将一个左值强制转化为右值引用,从而可以通过右值引用使用该值,用于移动语义
static_cast(lvalue);
//如果Copyable没有移动构造函数,那么将调用以常量左值引用为参数的拷贝构造函数。
Copyable news = std::move(s);
RVO(return value optimization)或者NRVO(Named Return Value optimiation)称为返回值优化。
关闭返回值优化
-fno-elide-constructors
完美转发:在函数模版中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。
//参数在传给IrunCodeActually之前就产生一次额外的临时对象拷贝,谈不上完美。
template
void IamForwarding(T t) {
IrunCodeActually(t);
}
C++11中的引用折叠规则
typedef const int T;
typedef T& TR;
TR& v = 1;
TR的类型定义 | 声明v的类型 | v的实际类型 |
---|---|---|
T& | TR | A& |
T& | TR& | A& |
T& | TR&& | A& |
T&& | TR | A&& |
T&& | TR& | A& |
T&& | TR&& | A&& |
规则:一旦模版类型为T&,那么v的实际类型就是A&, 模版的类型为T&&, 实际类型为模版类型的&&加上声明v的类型&的个数(减去&&)( 定义中出现了左值引用,引用折叠总是优先将其折叠为左值引用)。
void IamForwording(T &&t) {
IrunCodeActually(static_cast(t));
}
等价于:
void IamForwording(T &&t) {
IrunCodeActually(forward(t));
}
void IamForwording(X& &&t) {
IrunCodeActually(static_cast(t));
}
引用折叠引用为,等价于:
void IamForwording(X&t) {
IrunCodeActually(static_cast(t));
}
void IamForwording(X&& &&t) {
IrunCodeActually(static_cast(t));
}
引用折叠引用为,等价于:
void IamForwording(X&& t) {
IrunCodeActually(static_cast(t));
}
C++11中将explicit的使用范围扩展到了自定义的类型转换操作符上。
class ConvertTo{};
class Convertable {
public:
explict operator ConvertTo() const {
return ConvertTo();
}
};
void Func(ConvertTo ct) {}
void test() {
Convertable c;
ConvertTo ct(c); //直接初始化构造
ConvertTo ct2 = c; //拷贝构造初始化失败,编译失败
ConvertTo ct3 = static_cast(c); //强制转化,通过
Func(c); //拷贝构造初始化失败,编译失败
}
C++11 支持{}花括号的初始化形式,列表初始化化使唯一一种可以防止类型收窄的初始化方式。
int a[] = {1, 3, 5};
int b[] = {2, 4, 6};
vector c{1, 3, 5};
map d = {{1, 1.0f}, {2, 2.0f}};
double *d = new double{1.2f};
只需要#include
enum Gender {boy, girl};
class People {
public:
People(initializer_list> lst) {
auto i = lst.begin();
for (; i != lst.end(); ++lst) {
data.push_back(*i);
}
}
private:
vector > data;
};
People ship = {{"Garfield", boy}, {"HelloKitty", girl}};
C++11将POD划分为两个基本概念集合:平凡的和标准布局的。
//使用类模版来帮助判断
template struct std::is_trivial;
//a和b是不同的权限,所以该匿名结构体不是标准布局
struct {
public:
int a;
private:
int b;
};
//A不是一个标准布局的类型,因为第一个非静态成员变量b的类型跟A所继承的类型B相同。
struct A : B { B b;}
//使用类模版来帮助判断
template struct std::is_standard_layout;
POD类型的好处:
C++ 98中,联合体内不允许有POD类型成员,不允许有静态或引用类型的成员。C++11中,任何非引用类型都可以成为联合体的数据成员。(实际可能不允许静态成员变量的存在,否则所有该类型的联合体将共享一个值)。
在C++ 11中,标准会默认删除一些非受限联合体的默认函数,如,非受限union有一个非POD成员,而该成员类型有非平凡的构造函数,那么非受限union的默认构造函数将被编译器删除。默认copy函数,copy赋值操作符以及析构函数等,也遵循此规则。
union myUnion
{
string s;
int i{1};
};
myUnion t; //compile error,因为myUnion的默认构造函数被删除了
解决办法,自己为非受限union定义构造函数
union myUnion
{
public:
myUnion()
{
new (&s) string("1233"); //placement new
}
~myUnion()
{
s.~string(); //析构
}
string s;
int i{1};
};
C++ 98不允许在不同的名字空间中对模板进行特化。
C++ 11中引入 inline namespace 內联名字空间,允许在父名字空间定义或特化子名字空间的模板。
#include
using namespace std;
namespace Jim {
inline namespace Basic {
struct Knife{};
class CorkScrew{};
};
inline namespace ToolKit {
template class SwissArmyKnife{};
}
namespace Other {
Knife b; // Knife in Basic
struct Knife{};
Knife c; //Knife in Other
Basic::Knife k; //Knife in Basic
}
}
namespace Jim {
template<> class SwissArmyKnife{};
}
using uint = unsigned int;
//在模版中使用using,typedef无法达到这样的效果
template
using MapString = std::map;
MapString numberedString;
SFINA(substitution failture is not an error,匹配失败不是错误)。这条规则是对重载的模版的参数进行展开的时候,如果展开导致一些类型不匹配,编译器不会报错。
struct Test {
typedef int foo;
};
template
void f(typename T::foo) {} //#1
template //#2
void f(T) {}
int main() {
f(10); //调用#1
f(10); //调用#2. 由于SFINA,虽然不存在类型int::foo,也不会发生编译错误
}
C++ 98中,一些在模版参数中使用表达式的情况,并没有被主流编译器支持。在C++ 11中,在模版参数替换时使用了表达式的情况下进行了明确规定,即表达式中没有出现“外部于表达式本身”的元素。如,发生一些模版的实例化,或者隐藏地产生一些拷贝函数,这样的模版推导都不会产生SFINAE失败(即编译器报错)。
template struct A {};
char xxx(int);
char xxx(float);
template
A f(T) {} //返回值为sizeof(xxx((T)0))为参数的类模版A
int main() {
f(1);
}