读《深入理解C++11》
Tips
- 
constexpr && const constexpr 关键字的引入是为了解决 const 对 ROM 支持的不友好。const 变量只是不可修改,并不是编译时常量,无法应用于 ROM 等小型设备 
- 
lambda && functor lambda 在内部被实现为仿函数 
- 
char&&wchar_tC++11 后 char 与 wchar_t进行连接时编译器将自动转化为wchar_t
- 
long long && 123LL 
- 
C++11 规定类的析构函数默认为 noexcept(true)
- 
C++11 的就地初始化比初始化列表先执行 
- 
C++11: sizeof(C::mem);C++98:sizeof((C*)(0)->mem)
- 
C++11: friend T;C++98:friend class T
- 
C++11: typeid(type).hash_code()
- 
C++11:常量表达式只能有一行 return 
- 
nullptr&&nullptr_t
- 
C++11 提供 unicode 支持 
- 
C++11 SFINAE 表达式中没有出现“ 外部于表达式本身” 的元素,比如说发生一些模板的实例化,或者隐式地产生一些 拷贝构造函数的话,这样的模板推导都不会产生 SFINAE 失败 
- 
extern template void fun<int>(int),少用
- 
内联名字空间 inline namespace
- 
构造函数的默认参数不会被继承。继承来的构造函数是隐式声明的 
- 
构造函数不能同时使用委派和初始化列表;委派构造函数可以捕获异常 
- 
auto && cv-qualifer 声明为引用类型 auto 会保持原有变量的属性 
- 
三元运算符的性能缺陷: #define Max(a,b) ((a)>(b)):(a):(b)
- 
纯右值 && 将亡值 将亡值是被强制转化为右值的返回值 
- 
万能的 cont T&C++ 98 开始就允许 const T& 绑定到右值,并可以延长右值的生命周期与引用值相同。这种优化减少了拷贝与析构的损耗。常量左值引用是“万能”的,如果重载没有实现右值引用的接口,则编译器自动调用常量左值引用接口 
- 
构造时为成员变量手动调用 move 编写右值接口时要显式的调用 move 函数将成员变量转化为右值以触发成员变量的移动语意 
- 
非受限联合体 / 类内匿名联合体 / placement new 任何非引用类型都可以成为联合体的数据成员,这样的联合体即所谓的非受限联合体( Unrestricted Union) 非受限联合体默认会删除非 POD 变量的构造函数,所以需要手动调用 placement new 和对应的析构 
- 
Big5 的自动生成机制 以在 C++11 中,拷贝构造/赋值和移动构造/赋值函数必须同时提供,或者同时不提供,程序员才能保证类同时具有拷贝和移动语义。只声明其中一种的话,类都仅能实现一种语义 
新概念
forward
所谓完美转发(perfectforwarding),是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数
完美转发的引入是为了解决右值属性传递的一些问题,按照右值的一般理解:没有名字不能取地址的值被称为右值,但右值参数在函数内是左值。比如函数 f(C &&c),c 在函数f 内部是当作左值看待的。如果在函数 f 内调用了另一个函数 g(C &&c)/g(const C &c),那么编译器默认会选择 g(const C &c) ,示例如下:
#include <utility>
#include <iostream>
class A{ };
void tt(A &&a)      { std::cout << "const A &&a" << std::endl; }
void tt(const A &a) { std::cout << "const A &a"  << std::endl; }
void bb(A &&a) { tt(a); } 
// void cc(A &&a) { tt(std::forward(a)); } // invalid usage of forward
template<typename T> 
void dd(T &&a) { tt(std::forward<T>(a)); } // 只有左值引用 forward 不需要模板参数,如下文定义
int main() {
  tt(A()); // const A &&a
  bb(A()); // const A &a
  dd(A()); // const A &&a
  A a;
  tt(a); // const A &a
  // bb(a); // invalid call
  dd(a); // const A &a
}
C++11 中 forward 的实现方式如下(注意第二种实现方法必须提供模板参数):
template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}
template <class T>
inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
    static_assert(
          !std::is_lvalue_reference<T>::value, "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}
为实现上述功能,C++ 11 在模板中引入了引用折叠的概念:
TR   R
T&   &  -> T&  // lvalue reference to cv TR -> lvalue reference to T
T&   && -> T&  // rvalue reference to cv TR -> TR (lvalue reference to T)
T&&  &  -> T&  // lvalue reference to cv TR -> lvalue reference to T
T&&  && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
initializer_list
#include <iostream>
#include <vector>
using namespace std;
class Mydata
{
public:
    Mydata &operator[](initializer_list<int> l) {
        for (auto i = l.begin(); i != l.end(); ++i) idx.push_back(*i);
        return *this;
    }
    Mydata &operator=(int v) {
        if (idx.empty() != true) {
            for (auto i = idx.begin(); i != idx.end(); ++i) {
                d.resize((*i > d.size()) ? *i : d.size());
                d[*i - 1] = v;
            }
            idx.clear();
        }
        return *this;
    }
    void Print() {
        for (auto i = d.begin(); i != d.end(); ++i)
            cout << *i << " ";
        cout << endl;
    }
private:
    vector<int> idx; // 辅助数组, 用于记录 index
    vector<int> d;
};
int main() {
    Mydata d;
    d[{2, 3, 5}] = 7;
    d[{1, 4, 5, 8}] = 4;
    d.Print(); // 4 7 7 4 4 0 0 4
}
自定义字面值
#include <iostream>
struct Watt{ unsigned int v; }; 
Watt operator "" _w( unsigned long long v) { 
    return {(unsigned int) v}; 
} 
int main() { 
    Watt capacity = 1024_w; 
}
decltype
编译时行为
decltype 的使用比较繁琐,非库代码一般较少使用
usingsize_t    = decltype(sizeof(0));
usingptrdiff_t = decltype((int*)0(int*)0);
usingnullptr_t = decltype(nullptr);
std::result_of
template <class> struct result_of;
template <class F, class... ArgTypes>
struct result_of<F(ArgTypes...)> {
  typedef decltype(std::declval<F>()(std::declval<ArgTypes>()...)) type;
};
返回值推导
template<typename T, typename U>
auto sum(T t, U u) -> decltype(t+u) { return t+u; }
变长模板
template <typename... Elements> class tuple; // 变长模板的声明 
template< typename Head, typename... Tail>   // 递归的偏特化定义 
class tuple<Head, Tail...> : private tuple< Tail...>  { Head head; }; 
template<> class tuple<> {}; // 边界条件
原子变量与内存序

default && delete && explicit
default 和 POD 类型息息相关
default 和默认 Big5 的自动生成规则息息相关
不要同时使用 explicit && default 或者 explicit && delete
class ConvType { 
    public: ConvType( int i) {}; 
    ConvType( char c) = delete; // 删除 char 版本 
};
alignas/alignof
因为硬件等原因 ,比如cache line (64B),总线宽度(64bit) 等,数据是否对齐会影响系统的性能,某些指令场景下未对齐的数据会造成系统的崩溃
以 64位 CPU 为例,cpu 以 64bit 为单位(8字节)从内存中读取数据,如果 int 类型的变量对齐到内存 1 字节地址边界,那么 cpu 在读取这个变量时很可能需要读两次,每次读 int 的一部分;如果 int 对齐到 4 字节边界,那么 CPU 一次肯定可以获取整个 int 变量(可以用反正法证明)
下面代码片段展示了 alignas/alignof 的使用方法。x64 cpu 支持矢量指令,一次可以操作多个变量,默认情况下以 8 字节为边界进行对齐,这不利于 CPU 取值(32 字节是扩展对齐方式,标准对这类行为没有定义)
#include <iostream> 
using namespace std; 
struct ColorVector { 
    double r, g, b, a; 
}; 
// 非标准对齐,使用时要注意
struct alignas(32) AlgColorVector { 
    double r, g, b, a; 
}; 
int main() { 
    // alignof(ColorVector): 8
	// alignof(AlgColorVector): 32
    // alignof(std::max_align_t): 16
    cout << "alignof(ColorVector): "      << alignof(ColorVector)    << endl;
    cout << "alignof(AlgColorVector): "   << alignof(AlgColorVector) << endl;
    cout << "alignof(std::max_align_t): " << alignof(max_align_t)    << endl;
    return 0; 
} 
std::max_align_t
系统支持的对齐数值是有限的,可以通过 alignof(std::max_align_t) 来判断对齐上限,通常是最大标量(long double)长度
使用超过默认对齐字节的对齐长度,标准不保证所有编译器正常
align/aligned_storage/aligned_union
// aligned_storage example
#include <iostream>
#include <type_traits>
struct A {  // non-POD type
  int avg;
  A (int a, int b) : avg((a+b)/2) {}
};
typedef std::aligned_storage<sizeof(A),alignof(A)>::type A_pod;
int main() {
  A_pod a,b;
  new (&a) A (10,20);
  b=a;
  std::cout << reinterpret_cast<A&>(b).avg << std::endl;
  return 0;
}
通用属性
如下程序片段,程序中 const 属性告诉编译器,该函数返回值只依赖于输入,不会改变函数外的数据,因此编译器可以对 area(3) 进行优化处理,只对函数调用一次,后续将 area(3) 视为常量进行操作,将大大提升程序性能
extern int area(int n) __attribute__ ((const))
int main()
{
    int areas=0;
    for(int i=0;i<10;++i)
    {
        areas+=area(3)+i;
    }
}
自C++11开始,C++ 拥有统一形式的通用属性申明方式,语法格式如下:
[[ carries_dependency ]] func();
C++ 11/14/17 以后常用属性有:
[[noreturn]]、[[carries_dependency]]、[[deprecated]]、[[deprecated(“reason”)]] 
POD
参考1:https://mariusbancila.ro/blog/2020/08/10/no-more-plain-old-data/
参考2:https://docs.microsoft.com/en-us/cpp/cpp/trivial-standard-layout-and-pod-types?view=msvc-160
参考3:https://en.cppreference.com/w/cpp/language/initialization
A POD type is a type that is both trivial and standard-layout. This definition must hold recursively for all its non-static data members.
POD 在 C++ 20 以后被 trivial 和 standard layout 这两个概念替换。简单一些可以认为 POD 类型是 C++ 中与 C 兼容的内存布局,没有 C 不理解的内存成分且拷贝没副作用(比如引用增加等)。以C++中的继承为例,因为 C 中没有继承等概念,所以 C++ 中有继承与多态而与 C 不兼容的变量都不是 POD,某些 OB 对象是 POD 但 OO 对象一定不是POD。可以通过 std::is_pod 判断是否 POD: std::is_pod = std::is_trivaial + std::is_standard_layout ,is_pod 慢慢被弃用,应该使用后两个方法
- 
trival , std::is_trivial&&=default没有自定义的 BIG5 和析构函数,且没有虚函数和虚基类 可以从一个例子来说明 trivial 的意义:对象的内存可以通过 malloc 创建然后通过 free 删除 default 关键字的存在可以将自定义了构造函数的类恢复为 POD。一般定义了自己的构造函数后默认的构造函数就不再生成,但使用 default 可以强制编译器生成默认构造等方法 
- 
standard layout, std::is_standard_layout,标准布局的对象内存是可预测的。不同编译器对 C++ 继承对象的实现方式不同,所以包含继承关系的对象一般不是标准布局,因为其内存布局强依赖编译器
POD 的优势
- 字节赋值,比如直接使用底层的 memory copy 进行拷贝
- 提供与 C 兼容的内存布局
- 保证静态初始化的安全有效