[{"data":1,"prerenderedAt":3388},["ShallowReactive",2],{"post-effective-modern-cplusplus":3,"home":3366},{"id":4,"title":5,"body":6,"date":3356,"description":3357,"extension":3358,"meta":3359,"navigation":54,"path":3360,"seo":3361,"stem":3362,"tags":3363,"__hash__":3365},"blog\u002Fblog\u002Feffective-modern-cplusplus.md","Effective Modern C++",{"type":7,"value":8,"toc":3296},"minimark",[9,14,19,62,67,74,84,142,146,155,159,216,262,265,292,301,322,329,359,362,365,411,425,429,494,498,537,541,545,549,553,642,646,650,726,730,734,832,836,840,848,852,856,860,864,900,904,908,912,916,949,953,966,973,980,1014,1027,1056,1092,1096,1100,1112,1140,1154,1161,1204,1210,1250,1254,1257,1345,1435,1438,1449,1456,1459,1467,1471,1484,1488,1515,1542,1545,1573,1576,1602,1606,1658,1662,1665,1668,1701,1704,1736,1739,1744,1747,1789,1792,1799,1802,1833,1837,1908,1950,1953,1957,1961,1964,2009,2012,2015,2019,2023,2027,2091,2095,2156,2160,2167,2176,2180,2184,2188,2216,2220,2240,2248,2263,2267,2270,2306,2312,2317,2320,2349,2368,2371,2375,2379,2382,2431,2441,2469,2472,2492,2495,2499,2502,2522,2525,2616,2620,2648,2652,2660,2664,2668,2685,2689,2699,2733,2736,2773,2777,2885,2900,2904,2913,2933,2937,2940,3099,3103,3117,3121,3125,3245,3250,3261,3265,3270,3292],[10,11,13],"h2",{"id":12},"chapter-1-deducing-types","CHAPTER 1 Deducing Types",[15,16,18],"h3",{"id":17},"条款1-理解模板类型推导","条款1 理解模板类型推导",[20,21,26],"pre",{"className":22,"code":23,"language":24,"meta":25,"style":25},"language-cpp shiki shiki-themes github-light github-dark","template\u003Ctypename T>\n\u002F\u002F 此处ParamType泛指param的类型，可以是T，T&，const T&以及T&&\nvoid f(ParamType param);\n\nf(expr)\n","cpp","",[27,28,29,37,43,49,56],"code",{"__ignoreMap":25},[30,31,34],"span",{"class":32,"line":33},"line",1,[30,35,36],{},"template\u003Ctypename T>\n",[30,38,40],{"class":32,"line":39},2,[30,41,42],{},"\u002F\u002F 此处ParamType泛指param的类型，可以是T，T&，const T&以及T&&\n",[30,44,46],{"class":32,"line":45},3,[30,47,48],{},"void f(ParamType param);\n",[30,50,52],{"class":32,"line":51},4,[30,53,55],{"emptyLinePlaceholder":54},true,"\n",[30,57,59],{"class":32,"line":58},5,[30,60,61],{},"f(expr)\n",[63,64,66],"h4",{"id":65},"paramtype为指针或引用但不是通用引用","ParamType为指针或引用但不是通用引用",[68,69,70],"blockquote",{},[71,72,73],"p",{},"通用引用为T&&",[75,76,77,81],"ol",{},[78,79,80],"li",{},"如果expr为引用，则忽略引用部分",[78,82,83],{},"剩下的部分决定T，然后T与形参匹配得出ParamType",[20,85,87],{"className":22,"code":86,"language":24,"meta":25,"style":25},"Template\u003Ctypename T>\nvoid f(T& param);\n\nint x = 1;\nconst int cx = 1;\nconst int& rx = cx;\n\nf(x);  \u002F\u002F T为int，param的类型为int&\nf(cx); \u002F\u002F T为const int，param类型为const int&\nf(rx); \u002F\u002F T为const int，param类型为const int&\n",[27,88,89,94,99,103,108,113,119,124,130,136],{"__ignoreMap":25},[30,90,91],{"class":32,"line":33},[30,92,93],{},"Template\u003Ctypename T>\n",[30,95,96],{"class":32,"line":39},[30,97,98],{},"void f(T& param);\n",[30,100,101],{"class":32,"line":45},[30,102,55],{"emptyLinePlaceholder":54},[30,104,105],{"class":32,"line":51},[30,106,107],{},"int x = 1;\n",[30,109,110],{"class":32,"line":58},[30,111,112],{},"const int cx = 1;\n",[30,114,116],{"class":32,"line":115},6,[30,117,118],{},"const int& rx = cx;\n",[30,120,122],{"class":32,"line":121},7,[30,123,55],{"emptyLinePlaceholder":54},[30,125,127],{"class":32,"line":126},8,[30,128,129],{},"f(x);  \u002F\u002F T为int，param的类型为int&\n",[30,131,133],{"class":32,"line":132},9,[30,134,135],{},"f(cx); \u002F\u002F T为const int，param类型为const int&\n",[30,137,139],{"class":32,"line":138},10,[30,140,141],{},"f(rx); \u002F\u002F T为const int，param类型为const int&\n",[63,143,145],{"id":144},"paramtype是通用引用","ParamType是通用引用",[147,148,149,152],"ul",{},[78,150,151],{},"expr是左值，T和ParamType都是左值引用",[78,153,154],{},"expr是右值，T为expr类型，ParamType为右值引用",[63,156,158],{"id":157},"paramtype既不是指针也不是引用","ParamType既不是指针也不是引用",[20,160,162],{"className":22,"code":161,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid f(T param);\n\nint x = 27;\nconst int cx = x;\nconst int& rx = cx;\n\n\u002F\u002F T和param都是int\nf(x);\nf(cx);\nf(rx)\n",[27,163,164,168,173,177,182,187,191,195,200,205,210],{"__ignoreMap":25},[30,165,166],{"class":32,"line":33},[30,167,36],{},[30,169,170],{"class":32,"line":39},[30,171,172],{},"void f(T param);\n",[30,174,175],{"class":32,"line":45},[30,176,55],{"emptyLinePlaceholder":54},[30,178,179],{"class":32,"line":51},[30,180,181],{},"int x = 27;\n",[30,183,184],{"class":32,"line":58},[30,185,186],{},"const int cx = x;\n",[30,188,189],{"class":32,"line":115},[30,190,118],{},[30,192,193],{"class":32,"line":121},[30,194,55],{"emptyLinePlaceholder":54},[30,196,197],{"class":32,"line":126},[30,198,199],{},"\u002F\u002F T和param都是int\n",[30,201,202],{"class":32,"line":132},[30,203,204],{},"f(x);\n",[30,206,207],{"class":32,"line":138},[30,208,209],{},"f(cx);\n",[30,211,213],{"class":32,"line":212},11,[30,214,215],{},"f(rx)\n",[75,217,218,221],{},[78,219,220],{},"如果expr是引用，忽略引用部分",[78,222,223,224,227,228,233,255,257,258,261],{},"如果忽略引用后是const或volatile，它们也会被忽略",[225,226],"br",{},"param只是expr的拷贝，expr不可修改，不代表param也一样",[68,229,230],{},[71,231,232],{},"函数实参传递给形参时，会忽略实参的顶层const",[20,234,236],{"className":22,"code":235,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid f(T param);\n\nconst char* const ptr = \"Fun with pointers\";\n",[27,237,238,242,246,250],{"__ignoreMap":25},[30,239,240],{"class":32,"line":33},[30,241,36],{},[30,243,244],{"class":32,"line":39},[30,245,172],{},[30,247,248],{"class":32,"line":45},[30,249,55],{"emptyLinePlaceholder":54},[30,251,252],{"class":32,"line":51},[30,253,254],{},"const char* const ptr = \"Fun with pointers\";\n",[225,256],{},"即T为",[27,259,260],{},"const char*","类型",[63,263,264],{"id":264},"数组实参",[20,266,268],{"className":22,"code":267,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid f(T param);\n\nconst char name[] = \"Effective\";\nf(name);\n",[27,269,270,274,278,282,287],{"__ignoreMap":25},[30,271,272],{"class":32,"line":33},[30,273,36],{},[30,275,276],{"class":32,"line":39},[30,277,172],{},[30,279,280],{"class":32,"line":45},[30,281,55],{"emptyLinePlaceholder":54},[30,283,284],{"class":32,"line":51},[30,285,286],{},"const char name[] = \"Effective\";\n",[30,288,289],{"class":32,"line":58},[30,290,291],{},"f(name);\n",[71,293,294,295,297,298,300],{},"此时，name会由数组退化成",[27,296,260],{},"指针即T为",[27,299,260],{},"类型。但同时也可用引用来表示数组",[20,302,304],{"className":22,"code":303,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid f(T& param);\n\nf(name);\n",[27,305,306,310,314,318],{"__ignoreMap":25},[30,307,308],{"class":32,"line":33},[30,309,36],{},[30,311,312],{"class":32,"line":39},[30,313,98],{},[30,315,316],{"class":32,"line":45},[30,317,55],{"emptyLinePlaceholder":54},[30,319,320],{"class":32,"line":51},[30,321,291],{},[71,323,324,325,328],{},"此时的T为数组引用类型，即",[27,326,327],{},"const char(&)[10]","，同时我们可以在模板函数中推出数组大小",[20,330,332],{"className":22,"code":331,"language":24,"meta":25,"style":25},"template\u003Ctypename T, std::size_t N>\nconstexpr std::size_t arraySize(T (&)[N]) noexcept\n{\n  return N;\n}\n",[27,333,334,339,344,349,354],{"__ignoreMap":25},[30,335,336],{"class":32,"line":33},[30,337,338],{},"template\u003Ctypename T, std::size_t N>\n",[30,340,341],{"class":32,"line":39},[30,342,343],{},"constexpr std::size_t arraySize(T (&)[N]) noexcept\n",[30,345,346],{"class":32,"line":45},[30,347,348],{},"{\n",[30,350,351],{"class":32,"line":51},[30,352,353],{},"  return N;\n",[30,355,356],{"class":32,"line":58},[30,357,358],{},"}\n",[63,360,361],{"id":361},"函数实参",[71,363,364],{},"与数组一样，函数类型也会退化为函数指针",[20,366,368],{"className":22,"code":367,"language":24,"meta":25,"style":25},"void someFunc(int, double);\ntemplate\u003Ctypename T>\nvoid f1(T param);\n\ntemplate\u003Ctypename T>\nvoid f2(T& param);\n\nf1(someFunc);  \u002F\u002F ParamType为void(*)(int, double)\nf2(someFunc);  \u002F\u002F ParamType为void(&)(int, double)\n",[27,369,370,375,379,384,388,392,397,401,406],{"__ignoreMap":25},[30,371,372],{"class":32,"line":33},[30,373,374],{},"void someFunc(int, double);\n",[30,376,377],{"class":32,"line":39},[30,378,36],{},[30,380,381],{"class":32,"line":45},[30,382,383],{},"void f1(T param);\n",[30,385,386],{"class":32,"line":51},[30,387,55],{"emptyLinePlaceholder":54},[30,389,390],{"class":32,"line":58},[30,391,36],{},[30,393,394],{"class":32,"line":115},[30,395,396],{},"void f2(T& param);\n",[30,398,399],{"class":32,"line":121},[30,400,55],{"emptyLinePlaceholder":54},[30,402,403],{"class":32,"line":126},[30,404,405],{},"f1(someFunc);  \u002F\u002F ParamType为void(*)(int, double)\n",[30,407,408],{"class":32,"line":132},[30,409,410],{},"f2(someFunc);  \u002F\u002F ParamType为void(&)(int, double)\n",[147,412,413,416,419,422],{},[78,414,415],{},"在模板类型推导时，引用会被忽略",[78,417,418],{},"对于通用引用的推导，左值实参会被特殊对待",[78,420,421],{},"对于传值类型的推导，实参的常量性和易变性会被忽略",[78,423,424],{},"在模板类型推导时，数组或函数会退化为指针，除非被用于初始化引用",[15,426,428],{"id":427},"条款2-理解auto类型推导-item2","条款2 理解auto类型推导 {#item2}",[147,430,431,491],{},[78,432,433,434,437,438],{},"auto类型拖到通常和模板类型推导相同，但auto类型推导假定花括号初始化代表\n",[27,435,436],{},"std::initializer_list","而模板类型推导不这样做",[20,439,441],{"className":22,"code":440,"language":24,"meta":25,"style":25},"auto x = {1, 2, 3, 4}; \u002F\u002F x的类型为std::initializer_list\u003Cint>\n\ntemplate\u003Ctypename T>\nvoid f(T param);\n\nf({1, 2, 3, 4});  \u002F\u002F 不能推导出\n\ntemplate\u003Ctypename T>\nvoid f(std::initializer_list\u003CT> param);\n\nf({1, 2, 3, 4});  \u002F\u002F 此时可推出\n",[27,442,443,448,452,456,460,464,469,473,477,482,486],{"__ignoreMap":25},[30,444,445],{"class":32,"line":33},[30,446,447],{},"auto x = {1, 2, 3, 4}; \u002F\u002F x的类型为std::initializer_list\u003Cint>\n",[30,449,450],{"class":32,"line":39},[30,451,55],{"emptyLinePlaceholder":54},[30,453,454],{"class":32,"line":45},[30,455,36],{},[30,457,458],{"class":32,"line":51},[30,459,172],{},[30,461,462],{"class":32,"line":58},[30,463,55],{"emptyLinePlaceholder":54},[30,465,466],{"class":32,"line":115},[30,467,468],{},"f({1, 2, 3, 4});  \u002F\u002F 不能推导出\n",[30,470,471],{"class":32,"line":121},[30,472,55],{"emptyLinePlaceholder":54},[30,474,475],{"class":32,"line":126},[30,476,36],{},[30,478,479],{"class":32,"line":132},[30,480,481],{},"void f(std::initializer_list\u003CT> param);\n",[30,483,484],{"class":32,"line":138},[30,485,55],{"emptyLinePlaceholder":54},[30,487,488],{"class":32,"line":212},[30,489,490],{},"f({1, 2, 3, 4});  \u002F\u002F 此时可推出\n",[78,492,493],{},"在C++14中允许出现在函数返回值或者lambda函数形参中，但它的工作机制是模板类型推导的方案",[15,495,497],{"id":496},"条款3-理解decltype","条款3 理解decltype",[147,499,500,503,526],{},[78,501,502],{},"decltype总是不加修改的产生变量或表达式的类型",[78,504,505,506],{},"对于T类型的左值表达式，decltype总是产出T的引用即T&",[20,507,509],{"className":22,"code":508,"language":24,"meta":25,"style":25},"int a = 1;\ndecltype(a);    \u002F\u002F int\ndecltype((a));  \u002F\u002F int&\n",[27,510,511,516,521],{"__ignoreMap":25},[30,512,513],{"class":32,"line":33},[30,514,515],{},"int a = 1;\n",[30,517,518],{"class":32,"line":39},[30,519,520],{},"decltype(a);    \u002F\u002F int\n",[30,522,523],{"class":32,"line":45},[30,524,525],{},"decltype((a));  \u002F\u002F int&\n",[78,527,528,529],{},"C++14支持decltype(auto)，就像auto一样，推导出类型，但它使用自己独特规则进行推导",[68,530,531,534],{},[71,532,533],{},"单纯的auto与模板参数推导一样，会忽略引用",[71,535,536],{},"而在decltype(auto)中，auto说明类型会被推导，decltype说明会按decltype的规则推导",[15,538,540],{"id":539},"条款4-学会查看类型推导结果","条款4 学会查看类型推导结果",[10,542,544],{"id":543},"chapter-2-auto","CHAPTER 2 auto",[15,546,548],{"id":547},"条款5-优先考虑auto而非显示类型声明","条款5 优先考虑auto而非显示类型声明",[15,550,552],{"id":551},"条款6-auto推导若非己愿使用显示类型初始化惯用法","条款6 auto推导若非己愿，使用显示类型初始化惯用法",[147,554,555,630],{},[78,556,557,558,606,608,609,624,626,627],{},"不可见的代理类可能会使auto从表达式中推导出错误的类型",[20,559,561],{"className":22,"code":560,"language":24,"meta":25,"style":25},"namespace std {\n  template\u003Cclass Allocator>\n  class vector\u003Cbool, Allocator>{\n    public:\n    class reference {...};\n\n    reference operator[](size_type n);\n  }\n}\n",[27,562,563,568,573,578,583,588,592,597,602],{"__ignoreMap":25},[30,564,565],{"class":32,"line":33},[30,566,567],{},"namespace std {\n",[30,569,570],{"class":32,"line":39},[30,571,572],{},"  template\u003Cclass Allocator>\n",[30,574,575],{"class":32,"line":45},[30,576,577],{},"  class vector\u003Cbool, Allocator>{\n",[30,579,580],{"class":32,"line":51},[30,581,582],{},"    public:\n",[30,584,585],{"class":32,"line":58},[30,586,587],{},"    class reference {...};\n",[30,589,590],{"class":32,"line":115},[30,591,55],{"emptyLinePlaceholder":54},[30,593,594],{"class":32,"line":121},[30,595,596],{},"    reference operator[](size_type n);\n",[30,598,599],{"class":32,"line":126},[30,600,601],{},"  }\n",[30,603,604],{"class":32,"line":132},[30,605,358],{},[225,607],{},"reference就是vector的代理类，当使用",[20,610,612],{"className":22,"code":611,"language":24,"meta":25,"style":25},"std::vector\u003Cbool> feature(const Widget&);\nauto highPriority = feature(w)[5];\n",[27,613,614,619],{"__ignoreMap":25},[30,615,616],{"class":32,"line":33},[30,617,618],{},"std::vector\u003Cbool> feature(const Widget&);\n",[30,620,621],{"class":32,"line":39},[30,622,623],{},"auto highPriority = feature(w)[5];\n",[225,625],{},"此时期望的是highPriority为bool类型，但实际上auto推导的是",[27,628,629],{},"std::vector\u003Cbool>::reference",[78,631,632,633],{},"显式类型初始器惯用强制auto推导出想要的结果",[20,634,636],{"className":22,"code":635,"language":24,"meta":25,"style":25},"auto highPriority = static_cast\u003Cbool>(feature(w)[5]);\n",[27,637,638],{"__ignoreMap":25},[30,639,640],{"class":32,"line":33},[30,641,635],{},[10,643,645],{"id":644},"chapter-3-moving-to-modern-c","CHAPTER 3 Moving to Modern C++",[15,647,649],{"id":648},"条款7-区别使用和创建对象","条款7 区别使用()和{}创建对象",[147,651,652,660,708],{},[78,653,654,655],{},"括号初始化可防止变窄转换",[68,656,657],{},[71,658,659],{},"括号初始化指的是大括号",[78,661,662,663,665,666,705,707],{},"在构造函数重载中，括号初始化会与",[27,664,436],{},"参数匹配，即使其他构造函数时更好的选择",[20,667,669],{"className":22,"code":668,"language":24,"meta":25,"style":25},"class Widget\n{\npublic:\n  Widget(int i, double d);\n  Widget(std::initializer_list\u003Cbool> il);\n};\nWidget w{10, 5.0};\n",[27,670,671,676,680,685,690,695,700],{"__ignoreMap":25},[30,672,673],{"class":32,"line":33},[30,674,675],{},"class Widget\n",[30,677,678],{"class":32,"line":39},[30,679,348],{},[30,681,682],{"class":32,"line":45},[30,683,684],{},"public:\n",[30,686,687],{"class":32,"line":51},[30,688,689],{},"  Widget(int i, double d);\n",[30,691,692],{"class":32,"line":58},[30,693,694],{},"  Widget(std::initializer_list\u003Cbool> il);\n",[30,696,697],{"class":32,"line":115},[30,698,699],{},"};\n",[30,701,702],{"class":32,"line":121},[30,703,704],{},"Widget w{10, 5.0};\n",[225,706],{},"上述代码会匹配initializer_list参数的构造函数，而同时由于括号初始化禁止变窄转换，编译会失败",[78,709,710,711],{},"在模板类中选择使用小括号初始化或花括号初始化创建对象是一个挑战",[20,712,714],{"className":22,"code":713,"language":24,"meta":25,"style":25},"std::vector\u003Cint> a(10, 20); \u002F\u002F 10个20\nstd::vector\u003Cint> b{10, 20}; \u002F\u002F 10和20\n",[27,715,716,721],{"__ignoreMap":25},[30,717,718],{"class":32,"line":33},[30,719,720],{},"std::vector\u003Cint> a(10, 20); \u002F\u002F 10个20\n",[30,722,723],{"class":32,"line":39},[30,724,725],{},"std::vector\u003Cint> b{10, 20}; \u002F\u002F 10和20\n",[15,727,729],{"id":728},"条款8-优先考虑nullptr而非0和null","条款8 优先考虑nullptr而非0和NULL",[15,731,733],{"id":732},"条款9-优先考虑别名声明而非typedefs","条款9 优先考虑别名声明而非typedefs",[147,735,736,739],{},[78,737,738],{},"typedef不支持模板化，但是别名声明支持",[78,740,741,742,806,808,809],{},"别名模板避免了使用::type后缀，同时也就省去了typename的声明",[20,743,745],{"className":22,"code":744,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nstruct MyAllocList\n{\n  typedef std::list\u003CT, MyAlloc\u003CT>> type;\n};\nMyAllocList\u003CWidget>::type lw;\n\n\u002F\u002F 在模板中还需要加上typename\ntemplate\u003Ctypename T>\nclass Widget\n{\n  typename MyAllocList\u003CT>::type list;\n};\n",[27,746,747,751,756,760,765,769,774,778,783,787,791,795,801],{"__ignoreMap":25},[30,748,749],{"class":32,"line":33},[30,750,36],{},[30,752,753],{"class":32,"line":39},[30,754,755],{},"struct MyAllocList\n",[30,757,758],{"class":32,"line":45},[30,759,348],{},[30,761,762],{"class":32,"line":51},[30,763,764],{},"  typedef std::list\u003CT, MyAlloc\u003CT>> type;\n",[30,766,767],{"class":32,"line":58},[30,768,699],{},[30,770,771],{"class":32,"line":115},[30,772,773],{},"MyAllocList\u003CWidget>::type lw;\n",[30,775,776],{"class":32,"line":121},[30,777,55],{"emptyLinePlaceholder":54},[30,779,780],{"class":32,"line":126},[30,781,782],{},"\u002F\u002F 在模板中还需要加上typename\n",[30,784,785],{"class":32,"line":132},[30,786,36],{},[30,788,789],{"class":32,"line":138},[30,790,675],{},[30,792,793],{"class":32,"line":212},[30,794,348],{},[30,796,798],{"class":32,"line":797},12,[30,799,800],{},"  typename MyAllocList\u003CT>::type list;\n",[30,802,804],{"class":32,"line":803},13,[30,805,699],{},[225,807],{},"可直接使用using",[20,810,812],{"className":22,"code":811,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nusing MyAllocList = std::list\u003CT, MyAlloc\u003CT>>;\n\nMyAllocList\u003CWidget> lw;\n",[27,813,814,818,823,827],{"__ignoreMap":25},[30,815,816],{"class":32,"line":33},[30,817,36],{},[30,819,820],{"class":32,"line":39},[30,821,822],{},"using MyAllocList = std::list\u003CT, MyAlloc\u003CT>>;\n",[30,824,825],{"class":32,"line":45},[30,826,55],{"emptyLinePlaceholder":54},[30,828,829],{"class":32,"line":51},[30,830,831],{},"MyAllocList\u003CWidget> lw;\n",[15,833,835],{"id":834},"条款10-优先考虑限域枚举而非未限域枚举","条款10 优先考虑限域枚举而非未限域枚举",[15,837,839],{"id":838},"条款11-优先考虑使用deleted函数而非使用未定义的私有声明","条款11 优先考虑使用deleted函数而非使用未定义的私有声明",[147,841,842,845],{},[78,843,844],{},"比起声明函数private但不定义，使用deleted函数更好",[78,846,847],{},"任何函数都能delete，包括非成员函数和模板示例",[15,849,851],{"id":850},"条例12-使用override声明重载函数","条例12 使用override声明重载函数",[15,853,855],{"id":854},"条款13-优先考虑const_iterator而非iterator","条款13 优先考虑const_iterator而非iterator",[15,857,859],{"id":858},"条款14-如果函数不抛出异常请使用noexcept","条款14 如果函数不抛出异常请使用noexcept",[15,861,863],{"id":862},"条款15-尽可能的使用constexpr","条款15 尽可能的使用constexpr",[147,865,866,889],{},[78,867,868,869],{},"constexpr对象是const，它的值在编译期可知。但不是所有const对象都是constexpr",[20,870,872],{"className":22,"code":871,"language":24,"meta":25,"style":25},"int a;\nconst int b = a;\nconstexpr int c = b; \u002F\u002F 错误，b的值编译期不可知\n",[27,873,874,879,884],{"__ignoreMap":25},[30,875,876],{"class":32,"line":33},[30,877,878],{},"int a;\n",[30,880,881],{"class":32,"line":39},[30,882,883],{},"const int b = a;\n",[30,885,886],{"class":32,"line":45},[30,887,888],{},"constexpr int c = b; \u002F\u002F 错误，b的值编译期不可知\n",[78,890,891,892],{},"当传递编译期可知的值时，constexpr函数可以产出编译期可知的结果",[68,893,894,897],{},[71,895,896],{},"constexpr函数的实参在编译期可知时，其结果将在编译期计算",[71,898,899],{},"constexpr函数被编译期不可知值调用时，他就像普通函数一样，在运行时计算",[15,901,903],{"id":902},"条款16-让const成员函数线程安全","条款16 让const成员函数线程安全",[15,905,907],{"id":906},"条款17-理解特殊成员函数的生成","条款17 理解特殊成员函数的生成",[10,909,911],{"id":910},"chapter-4-smart-pointers","CHAPTER 4 Smart pointers",[15,913,915],{"id":914},"条款18-对于独占资源使用stdunique_ptr","条款18 对于独占资源使用std::unique_ptr",[147,917,918,939],{},[78,919,920,921,924,925],{},"默认情况，资源销毁通过delete，但支持自定义的删除函数。而有状态的删除器和函数指针会增加\n",[27,922,923],{},"std::unique_ptr","的大小",[68,926,927,930,933],{},[71,928,929],{},"有状态是指有状态对象，就是有数据对象，可以保持数据，是非线程安全的",[71,931,932],{},"无状态就是一次操作，不能保存数据",[71,934,935],{},[936,937,938],"del",{},"可能是这样的",[78,940,941,942,944,945,948],{},"将",[27,943,923],{},"转化为",[27,946,947],{},"std::shared_ptr","是简单的",[15,950,952],{"id":951},"条款19-对于共享资源使用stdshared_ptr","条款19 对于共享资源使用std::shared_ptr",[147,954,955,960,963],{},[78,956,957,959],{},[27,958,947],{},"大小是原始指针的两倍，内部包含一个指向资源的原始指针，一个指向引用计数\n的指针(指向控制块，控制块包含引用计数)",[78,961,962],{},"引用计数必须动态分配",[78,964,965],{},"递增递减引用计数必须是原子性的",[71,967,968],{},[969,970],"img",{"alt":971,"src":972},"share_ptr","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Fimages\u002Fshared_ptr.png",[71,974,975,976,979],{},"如果通过原始指针构造",[27,977,978],{},"shared_ptr","，需要直接传递new的结果",[20,981,983],{"className":22,"code":982,"language":24,"meta":25,"style":25},"std::shared_ptr\u003CWidget> sp(new Widget);\n\nWidget* pw = new Widget;\n\u002F\u002F 会创建两个控制块，当spw1释放后，spw2会重复释放\nstd::shared_ptr\u003CWidget> spw1(pw);\nstd::shared_ptr\u003CWidget> spw2(pw);\n",[27,984,985,990,994,999,1004,1009],{"__ignoreMap":25},[30,986,987],{"class":32,"line":33},[30,988,989],{},"std::shared_ptr\u003CWidget> sp(new Widget);\n",[30,991,992],{"class":32,"line":39},[30,993,55],{"emptyLinePlaceholder":54},[30,995,996],{"class":32,"line":45},[30,997,998],{},"Widget* pw = new Widget;\n",[30,1000,1001],{"class":32,"line":51},[30,1002,1003],{},"\u002F\u002F 会创建两个控制块，当spw1释放后，spw2会重复释放\n",[30,1005,1006],{"class":32,"line":58},[30,1007,1008],{},"std::shared_ptr\u003CWidget> spw1(pw);\n",[30,1010,1011],{"class":32,"line":115},[30,1012,1013],{},"std::shared_ptr\u003CWidget> spw2(pw);\n",[71,1015,1016,1017,1020,1021,1023,1024],{},"当需要在类内部通过",[27,1018,1019],{},"this","构造",[27,1022,978],{},"时，需要使用",[27,1025,1026],{},"share_from_this()",[20,1028,1030],{"className":22,"code":1029,"language":24,"meta":25,"style":25},"std::vector\u003Cstd::shared_ptr\u003CWidget>> widgets;\nvoid Widget::process() {\n  \u002F\u002F 会创建单独的控制块，造成重复释放\n  widgets.emplace_back(this);\n}\n",[27,1031,1032,1037,1042,1047,1052],{"__ignoreMap":25},[30,1033,1034],{"class":32,"line":33},[30,1035,1036],{},"std::vector\u003Cstd::shared_ptr\u003CWidget>> widgets;\n",[30,1038,1039],{"class":32,"line":39},[30,1040,1041],{},"void Widget::process() {\n",[30,1043,1044],{"class":32,"line":45},[30,1045,1046],{},"  \u002F\u002F 会创建单独的控制块，造成重复释放\n",[30,1048,1049],{"class":32,"line":51},[30,1050,1051],{},"  widgets.emplace_back(this);\n",[30,1053,1054],{"class":32,"line":58},[30,1055,358],{},[20,1057,1059],{"className":22,"code":1058,"language":24,"meta":25,"style":25},"class Widget: public std::enable_shared_from_this {\npublic:\n  void process();\n};\nvoid Widget::process() {\n  widgets.emplace_back(share_from_this());\n}\n",[27,1060,1061,1066,1070,1075,1079,1083,1088],{"__ignoreMap":25},[30,1062,1063],{"class":32,"line":33},[30,1064,1065],{},"class Widget: public std::enable_shared_from_this {\n",[30,1067,1068],{"class":32,"line":39},[30,1069,684],{},[30,1071,1072],{"class":32,"line":45},[30,1073,1074],{},"  void process();\n",[30,1076,1077],{"class":32,"line":51},[30,1078,699],{},[30,1080,1081],{"class":32,"line":58},[30,1082,1041],{},[30,1084,1085],{"class":32,"line":115},[30,1086,1087],{},"  widgets.emplace_back(share_from_this());\n",[30,1089,1090],{"class":32,"line":121},[30,1091,358],{},[15,1093,1095],{"id":1094},"条款20-当stdshared_ptr可能悬空时使用stdweak_ptr","条款20 当std::shared_ptr可能悬空时使用std::weak_ptr",[15,1097,1099],{"id":1098},"条款21-优先考虑使用stdmake_unique和stdmake_shared而非new","条款21 优先考虑使用std::make_unique和std::make_shared而非new",[71,1101,1102,1105,1106,1109,1110],{},[27,1103,1104],{},"std::make_shared","是C++11标准，但",[27,1107,1108],{},"std::make_unique","在C++14，但可自己实现基础版本的",[27,1111,1108],{},[20,1113,1115],{"className":22,"code":1114,"language":24,"meta":25,"style":25},"template\u003Ctypename T, typename... Ts>\nstd::unique_ptr\u003CT> make_unique(Ts&&... params)\n{\n  return std::unique_ptr\u003CT>(new T(std::forward\u003CTs>(params)...));\n}\n",[27,1116,1117,1122,1127,1131,1136],{"__ignoreMap":25},[30,1118,1119],{"class":32,"line":33},[30,1120,1121],{},"template\u003Ctypename T, typename... Ts>\n",[30,1123,1124],{"class":32,"line":39},[30,1125,1126],{},"std::unique_ptr\u003CT> make_unique(Ts&&... params)\n",[30,1128,1129],{"class":32,"line":45},[30,1130,348],{},[30,1132,1133],{"class":32,"line":51},[30,1134,1135],{},"  return std::unique_ptr\u003CT>(new T(std::forward\u003CTs>(params)...));\n",[30,1137,1138],{"class":32,"line":58},[30,1139,358],{},[71,1141,1142,1143,1146,1147,1150,1151,1153],{},"控制块还有第二个引用计数",[27,1144,1145],{},"weak_count","，只要",[27,1148,1149],{},"std::weak_ptr","引用一个控制块即",[27,1152,1145],{},"大于零，\n该控制块就必须存在。",[71,1155,1156,1157,1160],{},"使用",[27,1158,1159],{},"make_shared","创建对象时，对象销毁和释放内存之间会出现延迟",[20,1162,1164],{"className":22,"code":1163,"language":24,"meta":25,"style":25},"auto pBig = std::make_shared\u003CBigType>();\n... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n\n\u002F\u002F 此时，先前分配给对象以及控制块的内存还未释放\n\n... \u002F\u002F 最后一个std::weak_ptr销毁\n\u002F\u002F 控制块和对象内存释放\n",[27,1165,1166,1171,1176,1181,1185,1190,1194,1199],{"__ignoreMap":25},[30,1167,1168],{"class":32,"line":33},[30,1169,1170],{},"auto pBig = std::make_shared\u003CBigType>();\n",[30,1172,1173],{"class":32,"line":39},[30,1174,1175],{},"... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n",[30,1177,1178],{"class":32,"line":45},[30,1179,1180],{},"... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n",[30,1182,1183],{"class":32,"line":51},[30,1184,55],{"emptyLinePlaceholder":54},[30,1186,1187],{"class":32,"line":58},[30,1188,1189],{},"\u002F\u002F 此时，先前分配给对象以及控制块的内存还未释放\n",[30,1191,1192],{"class":32,"line":115},[30,1193,55],{"emptyLinePlaceholder":54},[30,1195,1196],{"class":32,"line":121},[30,1197,1198],{},"... \u002F\u002F 最后一个std::weak_ptr销毁\n",[30,1200,1201],{"class":32,"line":126},[30,1202,1203],{},"\u002F\u002F 控制块和对象内存释放\n",[71,1205,1206,1207,1209],{},"直接使用new，一旦最后一个",[27,1208,978],{},"被销毁，对象的内存就会释放",[20,1211,1213],{"className":22,"code":1212,"language":24,"meta":25,"style":25},"auto pBig = std::shared_ptr\u003CBigType>(new BigType);\n... \u002F\u002F 创建std::shared_ptr和std::weak_ptr指向对象\n... \u002F\u002F 当最后一个std::shared_ptr销毁，但std::weak_ptr还在\n\n\u002F\u002F 此时，对象销毁，分配给对象的内存释放\n\n... \u002F\u002F 最后一个std::weak_ptr销毁\n\u002F\u002F 控制块的内存释放\n",[27,1214,1215,1220,1224,1228,1232,1237,1241,1245],{"__ignoreMap":25},[30,1216,1217],{"class":32,"line":33},[30,1218,1219],{},"auto pBig = std::shared_ptr\u003CBigType>(new BigType);\n",[30,1221,1222],{"class":32,"line":39},[30,1223,1175],{},[30,1225,1226],{"class":32,"line":45},[30,1227,1180],{},[30,1229,1230],{"class":32,"line":51},[30,1231,55],{"emptyLinePlaceholder":54},[30,1233,1234],{"class":32,"line":58},[30,1235,1236],{},"\u002F\u002F 此时，对象销毁，分配给对象的内存释放\n",[30,1238,1239],{"class":32,"line":115},[30,1240,55],{"emptyLinePlaceholder":54},[30,1242,1243],{"class":32,"line":121},[30,1244,1198],{},[30,1246,1247],{"class":32,"line":126},[30,1248,1249],{},"\u002F\u002F 控制块的内存释放\n",[15,1251,1253],{"id":1252},"条款22-当使用pimpl惯用法请在实现文件中定义特殊成员函数","条款22 当使用Pimpl惯用法，请在实现文件中定义特殊成员函数",[71,1255,1256],{},"Pimpl是\"指向实现的指针\"，通过将类的实现细节放在一个单独的实现类当中，类通过private指针类来访问实现类",[20,1258,1260],{"className":22,"code":1259,"language":24,"meta":25,"style":25},"\u002F\u002F widget.h\n#include \u003Cmemory>\n\nclass Widget\n{\npublic:\n  Widget();\n  ~Widget();\n  Widget(const Widget&);\n  Widget& operator=(const Widget&);\n  Widget(Widget&&);\n  Widget& operator=(Widget&&);\n\nprivate:\n  struct Impl;\n  std::unique_ptr\u003CImpl> pImpl;\n};\n",[27,1261,1262,1267,1272,1276,1280,1284,1288,1293,1298,1303,1308,1313,1318,1322,1328,1334,1340],{"__ignoreMap":25},[30,1263,1264],{"class":32,"line":33},[30,1265,1266],{},"\u002F\u002F widget.h\n",[30,1268,1269],{"class":32,"line":39},[30,1270,1271],{},"#include \u003Cmemory>\n",[30,1273,1274],{"class":32,"line":45},[30,1275,55],{"emptyLinePlaceholder":54},[30,1277,1278],{"class":32,"line":51},[30,1279,675],{},[30,1281,1282],{"class":32,"line":58},[30,1283,348],{},[30,1285,1286],{"class":32,"line":115},[30,1287,684],{},[30,1289,1290],{"class":32,"line":121},[30,1291,1292],{},"  Widget();\n",[30,1294,1295],{"class":32,"line":126},[30,1296,1297],{},"  ~Widget();\n",[30,1299,1300],{"class":32,"line":132},[30,1301,1302],{},"  Widget(const Widget&);\n",[30,1304,1305],{"class":32,"line":138},[30,1306,1307],{},"  Widget& operator=(const Widget&);\n",[30,1309,1310],{"class":32,"line":212},[30,1311,1312],{},"  Widget(Widget&&);\n",[30,1314,1315],{"class":32,"line":797},[30,1316,1317],{},"  Widget& operator=(Widget&&);\n",[30,1319,1320],{"class":32,"line":803},[30,1321,55],{"emptyLinePlaceholder":54},[30,1323,1325],{"class":32,"line":1324},14,[30,1326,1327],{},"private:\n",[30,1329,1331],{"class":32,"line":1330},15,[30,1332,1333],{},"  struct Impl;\n",[30,1335,1337],{"class":32,"line":1336},16,[30,1338,1339],{},"  std::unique_ptr\u003CImpl> pImpl;\n",[30,1341,1343],{"class":32,"line":1342},17,[30,1344,699],{},[20,1346,1348],{"className":22,"code":1347,"language":24,"meta":25,"style":25},"\u002F\u002F widget.cpp\n#include \"widget.h\"\n\nstruct Widget::Impl\n{\n  int x, y;\n};\n\nWidget::Widget(): pImpl(std::make_unique\u003CImpl>()) {}\nWidget::~Widget() = default;\nWidget::Widget(const Widget& rhs): pImpl(std::make_unique\u003CImpl>(*rhs.pImpl)) {}\nWidget& Widget::operator=(const Widget& rhs)\n{\n  *pImpl = *rhs.pImpl;\n  return *this;\n}\nWidget(Widget&& rhs) = default;\nWidget& operator=(Widget&& rhs) = default;\n",[27,1349,1350,1355,1360,1364,1369,1373,1378,1382,1386,1391,1396,1401,1406,1410,1415,1420,1424,1429],{"__ignoreMap":25},[30,1351,1352],{"class":32,"line":33},[30,1353,1354],{},"\u002F\u002F widget.cpp\n",[30,1356,1357],{"class":32,"line":39},[30,1358,1359],{},"#include \"widget.h\"\n",[30,1361,1362],{"class":32,"line":45},[30,1363,55],{"emptyLinePlaceholder":54},[30,1365,1366],{"class":32,"line":51},[30,1367,1368],{},"struct Widget::Impl\n",[30,1370,1371],{"class":32,"line":58},[30,1372,348],{},[30,1374,1375],{"class":32,"line":115},[30,1376,1377],{},"  int x, y;\n",[30,1379,1380],{"class":32,"line":121},[30,1381,699],{},[30,1383,1384],{"class":32,"line":126},[30,1385,55],{"emptyLinePlaceholder":54},[30,1387,1388],{"class":32,"line":132},[30,1389,1390],{},"Widget::Widget(): pImpl(std::make_unique\u003CImpl>()) {}\n",[30,1392,1393],{"class":32,"line":138},[30,1394,1395],{},"Widget::~Widget() = default;\n",[30,1397,1398],{"class":32,"line":212},[30,1399,1400],{},"Widget::Widget(const Widget& rhs): pImpl(std::make_unique\u003CImpl>(*rhs.pImpl)) {}\n",[30,1402,1403],{"class":32,"line":797},[30,1404,1405],{},"Widget& Widget::operator=(const Widget& rhs)\n",[30,1407,1408],{"class":32,"line":803},[30,1409,348],{},[30,1411,1412],{"class":32,"line":1324},[30,1413,1414],{},"  *pImpl = *rhs.pImpl;\n",[30,1416,1417],{"class":32,"line":1330},[30,1418,1419],{},"  return *this;\n",[30,1421,1422],{"class":32,"line":1336},[30,1423,358],{},[30,1425,1426],{"class":32,"line":1342},[30,1427,1428],{},"Widget(Widget&& rhs) = default;\n",[30,1430,1432],{"class":32,"line":1431},18,[30,1433,1434],{},"Widget& operator=(Widget&& rhs) = default;\n",[71,1436,1437],{},"当一个Widget对象销毁时",[75,1439,1440,1443,1446],{},[78,1441,1442],{},"会调用析构函数销毁pImpl",[78,1444,1445],{},"pImpl调用默认删除器",[78,1447,1448],{},"默认删除器使用delete释放原始指针所指向的空间",[71,1450,1451,1452,1455],{},"在默认删除器中，delete之前会调用",[27,1453,1454],{},"static_assert","来确保原始指针指向的类型不是一个未完成类型，\n因此应该在调用析构函数之前，让Impl为完整类型。即，将析构函数的定义写在Impl的定义下方",[71,1457,1458],{},"对于移动操作，需要销毁原来的对象，因此与析构相同",[68,1460,1461],{},[71,1462,1463,1464,1466],{},"对于",[27,1465,947],{},"来说，删除器的类型不是智能指针的一部分，在特殊函数(析构，移动)调用时，\n不需要指向的对象是完成类型",[10,1468,1470],{"id":1469},"chapter-5-rvalue-references-move-semantics-and-perfect-forwarding","CHAPTER 5 RValue References, Move Semantics and Perfect Forwarding",[68,1472,1473],{},[71,1474,1475,1479,1480,1483],{},[1476,1477,1478],"strong",{},"参数","(parameter)永远是",[1476,1481,1482],{},"左值","(LValue)，即便它的类型是一个右值引用",[15,1485,1487],{"id":1486},"条款23-理解stdmove和stdforward","条款23 理解std::move和std::forward",[20,1489,1491],{"className":22,"code":1490,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\ntypename remove_reference\u003CT>::type&& move(T&& param)\n{\n  return static_cast\u003Cremove_reference\u003CT>::type&&>(param);\n}\n",[27,1492,1493,1497,1502,1506,1511],{"__ignoreMap":25},[30,1494,1495],{"class":32,"line":33},[30,1496,36],{},[30,1498,1499],{"class":32,"line":39},[30,1500,1501],{},"typename remove_reference\u003CT>::type&& move(T&& param)\n",[30,1503,1504],{"class":32,"line":45},[30,1505,348],{},[30,1507,1508],{"class":32,"line":51},[30,1509,1510],{},"  return static_cast\u003Cremove_reference\u003CT>::type&&>(param);\n",[30,1512,1513],{"class":32,"line":58},[30,1514,358],{},[20,1516,1518],{"className":22,"code":1517,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid Foo(T&& param)\n{\n  Test(std::forward\u003CT>(param))\n}\n",[27,1519,1520,1524,1529,1533,1538],{"__ignoreMap":25},[30,1521,1522],{"class":32,"line":33},[30,1523,36],{},[30,1525,1526],{"class":32,"line":39},[30,1527,1528],{},"void Foo(T&& param)\n",[30,1530,1531],{"class":32,"line":45},[30,1532,348],{},[30,1534,1535],{"class":32,"line":51},[30,1536,1537],{},"  Test(std::forward\u003CT>(param))\n",[30,1539,1540],{"class":32,"line":58},[30,1541,358],{},[71,1543,1544],{},"在对通用引用转发时，实参无论是左值还是右值，都会被T&&接受，但当接收右值时，param的类型为\n右值引用，但此时param为一个左值，因此forward匹配的仍然是形参为左值引用的重载",[20,1546,1548],{"className":22,"code":1547,"language":24,"meta":25,"style":25},"template \u003Ctypename T>\nT&& forward(remove_reference\u003CT>::type& param)\n{\n  return static_cast\u003CT&&>(param);\n}\n",[27,1549,1550,1555,1560,1564,1569],{"__ignoreMap":25},[30,1551,1552],{"class":32,"line":33},[30,1553,1554],{},"template \u003Ctypename T>\n",[30,1556,1557],{"class":32,"line":39},[30,1558,1559],{},"T&& forward(remove_reference\u003CT>::type& param)\n",[30,1561,1562],{"class":32,"line":45},[30,1563,348],{},[30,1565,1566],{"class":32,"line":51},[30,1567,1568],{},"  return static_cast\u003CT&&>(param);\n",[30,1570,1571],{"class":32,"line":58},[30,1572,358],{},[71,1574,1575],{},"该重载只有在传入右值的时候才会匹配，并且返回右值",[20,1577,1579],{"className":22,"code":1578,"language":24,"meta":25,"style":25},"template \u003Ctypename T>\nT&& forward(remove_reference\u003CT>::type&& param)\n{\n  return static_cast\u003CT&&>(param);\n}\n",[27,1580,1581,1585,1590,1594,1598],{"__ignoreMap":25},[30,1582,1583],{"class":32,"line":33},[30,1584,1554],{},[30,1586,1587],{"class":32,"line":39},[30,1588,1589],{},"T&& forward(remove_reference\u003CT>::type&& param)\n",[30,1591,1592],{"class":32,"line":45},[30,1593,348],{},[30,1595,1596],{"class":32,"line":51},[30,1597,1568],{},[30,1599,1600],{"class":32,"line":58},[30,1601,358],{},[15,1603,1605],{"id":1604},"条款24-区分通用引用与右值引用","条款24 区分通用引用与右值引用",[147,1607,1608,1611],{},[78,1609,1610],{},"如果一个函数模板参数的类型为T&&，并且T需要被推导得知，或者如果一个对象被声明为auto&&，这个\n参数或者对象就是一个通用引用",[78,1612,1613,1614,1619,1655,1657],{},"如果类型声明的形式不是标准的type&&，或者如果类型推导没有发生，那么type&&代表一个右值引用",[68,1615,1616],{},[71,1617,1618],{},"模板里面的函数参数类型为T&&，并不一定会发生类型推导",[20,1620,1622],{"className":22,"code":1621,"language":24,"meta":25,"style":25},"template \u003Ctypename T>\nclass vector\n{\npublic:\n  void push_back(T&& param);\n};\nstd::vector\u003CWidget> v;\n",[27,1623,1624,1628,1633,1637,1641,1646,1650],{"__ignoreMap":25},[30,1625,1626],{"class":32,"line":33},[30,1627,1554],{},[30,1629,1630],{"class":32,"line":39},[30,1631,1632],{},"class vector\n",[30,1634,1635],{"class":32,"line":45},[30,1636,348],{},[30,1638,1639],{"class":32,"line":51},[30,1640,684],{},[30,1642,1643],{"class":32,"line":58},[30,1644,1645],{},"  void push_back(T&& param);\n",[30,1647,1648],{"class":32,"line":115},[30,1649,699],{},[30,1651,1652],{"class":32,"line":121},[30,1653,1654],{},"std::vector\u003CWidget> v;\n",[225,1656],{},"实例化vector时就确定了push_back的声明",[15,1659,1661],{"id":1660},"条款25-对右值引用使用stdmove对通用引用使用stdforward","条款25 对右值引用使用std::move，对通用引用使用std::forward",[71,1663,1664],{},"按值返回的函数，并且返回值绑定到右值引用或通用引用上，需要对返回值的引用使用std::move或者\nstd::forward",[71,1666,1667],{},"lhs为左值，返回lhs会拷贝到返回值的内存空间，而如果lhs支持移动，使用std::move效率更高",[20,1669,1671],{"className":22,"code":1670,"language":24,"meta":25,"style":25},"Matrix operator+(Matrix&& lhs, const Matrix& rhs)\n{\n  lhs += rhs;\n  \u002F\u002F return lhs;\n  return std::move(lhs);\n}\n",[27,1672,1673,1678,1682,1687,1692,1697],{"__ignoreMap":25},[30,1674,1675],{"class":32,"line":33},[30,1676,1677],{},"Matrix operator+(Matrix&& lhs, const Matrix& rhs)\n",[30,1679,1680],{"class":32,"line":39},[30,1681,348],{},[30,1683,1684],{"class":32,"line":45},[30,1685,1686],{},"  lhs += rhs;\n",[30,1688,1689],{"class":32,"line":51},[30,1690,1691],{},"  \u002F\u002F return lhs;\n",[30,1693,1694],{"class":32,"line":58},[30,1695,1696],{},"  return std::move(lhs);\n",[30,1698,1699],{"class":32,"line":115},[30,1700,358],{},[71,1702,1703],{},"如果不带std::forward，frac类型为右值引用时，frac仍然为左值，return仍需要拷贝",[20,1705,1707],{"className":22,"code":1706,"language":24,"meta":25,"style":25},"template \u003Ctypename T>\nFraction reduceAndCopy(T&& frac)\n{\n  ...\n  return std::forward\u003CT>(frac);\n}\n",[27,1708,1709,1713,1718,1722,1727,1732],{"__ignoreMap":25},[30,1710,1711],{"class":32,"line":33},[30,1712,1554],{},[30,1714,1715],{"class":32,"line":39},[30,1716,1717],{},"Fraction reduceAndCopy(T&& frac)\n",[30,1719,1720],{"class":32,"line":45},[30,1721,348],{},[30,1723,1724],{"class":32,"line":51},[30,1725,1726],{},"  ...\n",[30,1728,1729],{"class":32,"line":58},[30,1730,1731],{},"  return std::forward\u003CT>(frac);\n",[30,1733,1734],{"class":32,"line":115},[30,1735,358],{},[71,1737,1738],{},"C++标准存在返回值优化(RVO)，即直接在返回值的内存中构造，来避免复制，也称为Copy elision",[68,1740,1741],{},[71,1742,1743],{},"具名返回值优化NRVO，返回具名的局部变量",[71,1745,1746],{},"返回值优化的条件：1.局部变量与返回值类型相同；2.局部变量就是返回值",[20,1748,1750],{"className":22,"code":1749,"language":24,"meta":25,"style":25},"Widget makeWidget()\n{\n  Widget w;\n  ...\n  \u002F\u002F 不满足要求2，std::move是对w的引用，而非局部变量，无法优化\n  \u002F\u002F return std::move(w);\n  return w;\n}\n",[27,1751,1752,1757,1761,1766,1770,1775,1780,1785],{"__ignoreMap":25},[30,1753,1754],{"class":32,"line":33},[30,1755,1756],{},"Widget makeWidget()\n",[30,1758,1759],{"class":32,"line":39},[30,1760,348],{},[30,1762,1763],{"class":32,"line":45},[30,1764,1765],{},"  Widget w;\n",[30,1767,1768],{"class":32,"line":51},[30,1769,1726],{},[30,1771,1772],{"class":32,"line":58},[30,1773,1774],{},"  \u002F\u002F 不满足要求2，std::move是对w的引用，而非局部变量，无法优化\n",[30,1776,1777],{"class":32,"line":115},[30,1778,1779],{},"  \u002F\u002F return std::move(w);\n",[30,1781,1782],{"class":32,"line":121},[30,1783,1784],{},"  return w;\n",[30,1786,1787],{"class":32,"line":126},[30,1788,358],{},[71,1790,1791],{},"如果满足RVO的条件，但编译器选择不执行复制忽略，则必须将返回对象视为右值。标准要求RVO，\n忽略复制或者将std::move隐式应用于返回的本地对象",[71,1793,1794,1795,1798],{},"因此上述代码中，",[27,1796,1797],{},"return w;","如果不执行复制忽略的优化，就会自动将std::move隐式执行",[71,1800,1801],{},"按值传递参数的情况于此类似，它们没有RVO的资格，但是如果作为返回值，编译器会将其视为右值",[20,1803,1805],{"className":22,"code":1804,"language":24,"meta":25,"style":25},"Widget makeWidget(Widget w)\n{\n  return w;\n  \u002F\u002F 实际上，编译器的代码为\n  \u002F\u002F return std::move(w);\n}\n",[27,1806,1807,1812,1816,1820,1825,1829],{"__ignoreMap":25},[30,1808,1809],{"class":32,"line":33},[30,1810,1811],{},"Widget makeWidget(Widget w)\n",[30,1813,1814],{"class":32,"line":39},[30,1815,348],{},[30,1817,1818],{"class":32,"line":45},[30,1819,1784],{},[30,1821,1822],{"class":32,"line":51},[30,1823,1824],{},"  \u002F\u002F 实际上，编译器的代码为\n",[30,1826,1827],{"class":32,"line":58},[30,1828,1779],{},[30,1830,1831],{"class":32,"line":115},[30,1832,358],{},[15,1834,1836],{"id":1835},"条款26-避免在通用引用上重载","条款26 避免在通用引用上重载",[20,1838,1840],{"className":22,"code":1839,"language":24,"meta":25,"style":25},"class Person\n{\npublic:\n  template\u003Ctypename T>\n  Person(T&& n) : name(std::forward\u003CT>(n)) {} \u002F\u002F 通过名字构造\n  Person(int id);                             \u002F\u002F 通过id构造\n  Person(const Person&);\n  Person(Person&&);\n};\n\nPerson p1(\"Nancy\");\nPerson p2(p1);  \u002F\u002F 调用的是通用引用的构造函数，而非拷贝构造\nconst Person p3(\"aaa\");\nPerson p4(p3);  \u002F\u002F 正常匹配拷贝构造\n",[27,1841,1842,1847,1851,1855,1860,1865,1870,1875,1880,1884,1888,1893,1898,1903],{"__ignoreMap":25},[30,1843,1844],{"class":32,"line":33},[30,1845,1846],{},"class Person\n",[30,1848,1849],{"class":32,"line":39},[30,1850,348],{},[30,1852,1853],{"class":32,"line":45},[30,1854,684],{},[30,1856,1857],{"class":32,"line":51},[30,1858,1859],{},"  template\u003Ctypename T>\n",[30,1861,1862],{"class":32,"line":58},[30,1863,1864],{},"  Person(T&& n) : name(std::forward\u003CT>(n)) {} \u002F\u002F 通过名字构造\n",[30,1866,1867],{"class":32,"line":115},[30,1868,1869],{},"  Person(int id);                             \u002F\u002F 通过id构造\n",[30,1871,1872],{"class":32,"line":121},[30,1873,1874],{},"  Person(const Person&);\n",[30,1876,1877],{"class":32,"line":126},[30,1878,1879],{},"  Person(Person&&);\n",[30,1881,1882],{"class":32,"line":132},[30,1883,699],{},[30,1885,1886],{"class":32,"line":138},[30,1887,55],{"emptyLinePlaceholder":54},[30,1889,1890],{"class":32,"line":212},[30,1891,1892],{},"Person p1(\"Nancy\");\n",[30,1894,1895],{"class":32,"line":797},[30,1896,1897],{},"Person p2(p1);  \u002F\u002F 调用的是通用引用的构造函数，而非拷贝构造\n",[30,1899,1900],{"class":32,"line":803},[30,1901,1902],{},"const Person p3(\"aaa\");\n",[30,1904,1905],{"class":32,"line":1324},[30,1906,1907],{},"Person p4(p3);  \u002F\u002F 正常匹配拷贝构造\n",[20,1909,1911],{"className":22,"code":1910,"language":24,"meta":25,"style":25},"class Person;\nclass SpecialPerson : public Person\n{\npublic:\n  \u002F\u002F 调用的都是Person的通用引用的构造函数\n  SpecialPerson(const SpecialPerson& rhs) : Person(rhs) {}\n  SpecialPerson(SpecialPerson&& rhs) : Person(std::move(rhs)) {}\n};\n",[27,1912,1913,1918,1923,1927,1931,1936,1941,1946],{"__ignoreMap":25},[30,1914,1915],{"class":32,"line":33},[30,1916,1917],{},"class Person;\n",[30,1919,1920],{"class":32,"line":39},[30,1921,1922],{},"class SpecialPerson : public Person\n",[30,1924,1925],{"class":32,"line":45},[30,1926,348],{},[30,1928,1929],{"class":32,"line":51},[30,1930,684],{},[30,1932,1933],{"class":32,"line":58},[30,1934,1935],{},"  \u002F\u002F 调用的都是Person的通用引用的构造函数\n",[30,1937,1938],{"class":32,"line":115},[30,1939,1940],{},"  SpecialPerson(const SpecialPerson& rhs) : Person(rhs) {}\n",[30,1942,1943],{"class":32,"line":121},[30,1944,1945],{},"  SpecialPerson(SpecialPerson&& rhs) : Person(std::move(rhs)) {}\n",[30,1947,1948],{"class":32,"line":126},[30,1949,699],{},[71,1951,1952],{},"完美转发构造函数是糟糕的实现，因为对于non-const左值不会调用构造函数而是完美转发构造，\n而且会劫持派生类对于基类的拷贝和移动构造",[15,1954,1956],{"id":1955},"条款27-熟悉通用引用重载的替代方法","条款27 熟悉通用引用重载的替代方法",[63,1958,1960],{"id":1959},"abandon-overloading","Abandon overloading",[71,1962,1963],{},"重载虽然可以区分std::string与int，而采用不同的处理方式，但如果需要类型转换时，可能不能\n起到期望的结果",[20,1965,1967],{"className":22,"code":1966,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid LogAndAdd(T&& name)\n{\n  names.emplace(std::forward\u003CT>(name));\n}\nvoid LogAndAdd(int id)\n{\n  names.emplace(GetNameById(id));\n}\n",[27,1968,1969,1973,1978,1982,1987,1991,1996,2000,2005],{"__ignoreMap":25},[30,1970,1971],{"class":32,"line":33},[30,1972,36],{},[30,1974,1975],{"class":32,"line":39},[30,1976,1977],{},"void LogAndAdd(T&& name)\n",[30,1979,1980],{"class":32,"line":45},[30,1981,348],{},[30,1983,1984],{"class":32,"line":51},[30,1985,1986],{},"  names.emplace(std::forward\u003CT>(name));\n",[30,1988,1989],{"class":32,"line":58},[30,1990,358],{},[30,1992,1993],{"class":32,"line":115},[30,1994,1995],{},"void LogAndAdd(int id)\n",[30,1997,1998],{"class":32,"line":121},[30,1999,348],{},[30,2001,2002],{"class":32,"line":126},[30,2003,2004],{},"  names.emplace(GetNameById(id));\n",[30,2006,2007],{"class":32,"line":132},[30,2008,358],{},[71,2010,2011],{},"此时如果实参类型为short，模板函数的优先级大于形参为int的重载",[71,2013,2014],{},"因此可以不使用重载，分别改函数名为logAndAddName和logAndAddId，但是如果是构造函数的话，\n就无法使用该方法",[63,2016,2018],{"id":2017},"pass-by-const-t","Pass by const T&",[63,2020,2022],{"id":2021},"pass-by-value","Pass by value",[63,2024,2026],{"id":2025},"use-tag-dispatch","Use Tag dispatch",[20,2028,2030],{"className":22,"code":2029,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nvoid LogAndAdd(T&& name)\n{\n  LogAndAddImpl(std::forward\u003CT>(name), std::is_integral\u003Cstd::remove_reference_t\u003CT>>());\n}\ntemplate\u003Ctypename T>\nvoid LogAndAddImpl(T&& name, std::false_type)\n{\n  ...\n}\nvoid LogAndAddImpl(int id, std::true_type)\n{\n  ...\n}\n",[27,2031,2032,2036,2040,2044,2049,2053,2057,2062,2066,2070,2074,2079,2083,2087],{"__ignoreMap":25},[30,2033,2034],{"class":32,"line":33},[30,2035,36],{},[30,2037,2038],{"class":32,"line":39},[30,2039,1977],{},[30,2041,2042],{"class":32,"line":45},[30,2043,348],{},[30,2045,2046],{"class":32,"line":51},[30,2047,2048],{},"  LogAndAddImpl(std::forward\u003CT>(name), std::is_integral\u003Cstd::remove_reference_t\u003CT>>());\n",[30,2050,2051],{"class":32,"line":58},[30,2052,358],{},[30,2054,2055],{"class":32,"line":115},[30,2056,36],{},[30,2058,2059],{"class":32,"line":121},[30,2060,2061],{},"void LogAndAddImpl(T&& name, std::false_type)\n",[30,2063,2064],{"class":32,"line":126},[30,2065,348],{},[30,2067,2068],{"class":32,"line":132},[30,2069,1726],{},[30,2071,2072],{"class":32,"line":138},[30,2073,358],{},[30,2075,2076],{"class":32,"line":212},[30,2077,2078],{},"void LogAndAddImpl(int id, std::true_type)\n",[30,2080,2081],{"class":32,"line":797},[30,2082,348],{},[30,2084,2085],{"class":32,"line":803},[30,2086,1726],{},[30,2088,2089],{"class":32,"line":1324},[30,2090,358],{},[63,2092,2094],{"id":2093},"constraining-templates-that-take-universal-references","Constraining templates that take universal references",[20,2096,2098],{"className":22,"code":2097,"language":24,"meta":25,"style":25},"class Person\n{\npublic:\n  template\u003Ctypename T, typename = std::enable_if_t\u003C\n    !std::is_base_of_v\u003CPerson, std::decay_t\u003CT>>\n    &&\n    !std::is_integral_v\u003Cstd::remove_reference\u003CT>>\n    >\n  >\n  Person(T&& n): name(std::forward\u003CT>(n)) {...}\n  Person(int id): name(GetNameById(id)) {...}\n};\n",[27,2099,2100,2104,2108,2112,2117,2122,2127,2132,2137,2142,2147,2152],{"__ignoreMap":25},[30,2101,2102],{"class":32,"line":33},[30,2103,1846],{},[30,2105,2106],{"class":32,"line":39},[30,2107,348],{},[30,2109,2110],{"class":32,"line":45},[30,2111,684],{},[30,2113,2114],{"class":32,"line":51},[30,2115,2116],{},"  template\u003Ctypename T, typename = std::enable_if_t\u003C\n",[30,2118,2119],{"class":32,"line":58},[30,2120,2121],{},"    !std::is_base_of_v\u003CPerson, std::decay_t\u003CT>>\n",[30,2123,2124],{"class":32,"line":115},[30,2125,2126],{},"    &&\n",[30,2128,2129],{"class":32,"line":121},[30,2130,2131],{},"    !std::is_integral_v\u003Cstd::remove_reference\u003CT>>\n",[30,2133,2134],{"class":32,"line":126},[30,2135,2136],{},"    >\n",[30,2138,2139],{"class":32,"line":132},[30,2140,2141],{},"  >\n",[30,2143,2144],{"class":32,"line":138},[30,2145,2146],{},"  Person(T&& n): name(std::forward\u003CT>(n)) {...}\n",[30,2148,2149],{"class":32,"line":212},[30,2150,2151],{},"  Person(int id): name(GetNameById(id)) {...}\n",[30,2153,2154],{"class":32,"line":797},[30,2155,699],{},[63,2157,2159],{"id":2158},"trade-offs","Trade-offs",[71,2161,2162,2163,2166],{},"使用Person(u\"hello\")，其中实参为",[27,2164,2165],{},"const char16_t","，而不是char，此时调用的是通用引用的构造函数，\n但其无法转换为std::string，因此需要提示错误信息",[20,2168,2170],{"className":22,"code":2169,"language":24,"meta":25,"style":25},"static_assert(std::is_constructible\u003Cstd::string, T>::value, \"message\");\n",[27,2171,2172],{"__ignoreMap":25},[30,2173,2174],{"class":32,"line":33},[30,2175,2169],{},[15,2177,2179],{"id":2178},"条款28-理解引用折叠","条款28 理解引用折叠",[15,2181,2183],{"id":2182},"条款29-移动语义的缺点","条款29 移动语义的缺点",[15,2185,2187],{"id":2186},"条款30-熟悉完美转发的失败情况","条款30 熟悉完美转发的失败情况",[20,2189,2191],{"className":22,"code":2190,"language":24,"meta":25,"style":25},"template\u003Ctypename... Ts>\nvoid fwd(Ts&&... params)\n{\n  f(std::forward\u003CTs>(params)...);\n}\n",[27,2192,2193,2198,2203,2207,2212],{"__ignoreMap":25},[30,2194,2195],{"class":32,"line":33},[30,2196,2197],{},"template\u003Ctypename... Ts>\n",[30,2199,2200],{"class":32,"line":39},[30,2201,2202],{},"void fwd(Ts&&... params)\n",[30,2204,2205],{"class":32,"line":45},[30,2206,348],{},[30,2208,2209],{"class":32,"line":51},[30,2210,2211],{},"  f(std::forward\u003CTs>(params)...);\n",[30,2213,2214],{"class":32,"line":58},[30,2215,358],{},[63,2217,2219],{"id":2218},"braced-initializers","Braced initializers",[20,2221,2223],{"className":22,"code":2222,"language":24,"meta":25,"style":25},"void f(const std::vector\u003Cint>& v);\nf({1, 2 , 3});  \u002F\u002F 隐式转换为std::vector\u003Cint>\nfwd({1, 2, 3}); \u002F\u002F 无法编译\n",[27,2224,2225,2230,2235],{"__ignoreMap":25},[30,2226,2227],{"class":32,"line":33},[30,2228,2229],{},"void f(const std::vector\u003Cint>& v);\n",[30,2231,2232],{"class":32,"line":39},[30,2233,2234],{},"f({1, 2 , 3});  \u002F\u002F 隐式转换为std::vector\u003Cint>\n",[30,2236,2237],{"class":32,"line":45},[30,2238,2239],{},"fwd({1, 2, 3}); \u002F\u002F 无法编译\n",[71,2241,2242,2243,2245,2246],{},"条款2中提到，在对fwd的调用中的{1, 2, 3}进行类型推导时，由于fwd的参数没有声明为",[27,2244,436],{},"，\n无法匹配。但auto却可以通过braced initializer推导出",[27,2247,436],{},[20,2249,2251],{"className":22,"code":2250,"language":24,"meta":25,"style":25},"auto il = {1, 2, 3}; \u002F\u002F il为std::initializer_list\u003Cint>类型\nfwd(il);\n",[27,2252,2253,2258],{"__ignoreMap":25},[30,2254,2255],{"class":32,"line":33},[30,2256,2257],{},"auto il = {1, 2, 3}; \u002F\u002F il为std::initializer_list\u003Cint>类型\n",[30,2259,2260],{"class":32,"line":39},[30,2261,2262],{},"fwd(il);\n",[63,2264,2266],{"id":2265},"_0或者null作为空指针","0或者NULL作为空指针",[63,2268,2269],{"id":2269},"仅声明的整数静态const数据成员",[20,2271,2273],{"className":22,"code":2272,"language":24,"meta":25,"style":25},"class Widget\n{\npublic:\n  static const std::size_t Minvals = 28;\n};\nvoid f(std::size_t val);\nfwd(Widget::Minvals);  \u002F\u002F 通过编译，但由于Minvals没有定义，无法链接\n",[27,2274,2275,2279,2283,2287,2292,2296,2301],{"__ignoreMap":25},[30,2276,2277],{"class":32,"line":33},[30,2278,675],{},[30,2280,2281],{"class":32,"line":39},[30,2282,348],{},[30,2284,2285],{"class":32,"line":45},[30,2286,684],{},[30,2288,2289],{"class":32,"line":51},[30,2290,2291],{},"  static const std::size_t Minvals = 28;\n",[30,2293,2294],{"class":32,"line":58},[30,2295,699],{},[30,2297,2298],{"class":32,"line":115},[30,2299,2300],{},"void f(std::size_t val);\n",[30,2302,2303],{"class":32,"line":121},[30,2304,2305],{},"fwd(Widget::Minvals);  \u002F\u002F 通过编译，但由于Minvals没有定义，无法链接\n",[71,2307,2308,2309,2311],{},"fwd的参数是通用引用，底层中，引用与指针是一样的，即通过引用传递Minvals实际上与使用指针转递\nMinvals一样。但是static const在类中仅仅只是声明，而声明是不会分配内存的，即无法被指针指向。",[225,2310],{},"\n从而，通过引用传递整型static const数据成员，必须定义它们",[68,2313,2314],{},[71,2315,2316],{},"只是要求定义，并不是强制，因为有的编译器允许未定义的情况",[63,2318,2319],{"id":2319},"重载的函数名称和模板名称",[20,2321,2323],{"className":22,"code":2322,"language":24,"meta":25,"style":25},"int Foo(int x);\nint Foo(int x, int y);\n\nf(int (*)(int));\nfwd(Foo); \u002F\u002F 无法判断选择哪个Foo\n",[27,2324,2325,2330,2335,2339,2344],{"__ignoreMap":25},[30,2326,2327],{"class":32,"line":33},[30,2328,2329],{},"int Foo(int x);\n",[30,2331,2332],{"class":32,"line":39},[30,2333,2334],{},"int Foo(int x, int y);\n",[30,2336,2337],{"class":32,"line":45},[30,2338,55],{"emptyLinePlaceholder":54},[30,2340,2341],{"class":32,"line":51},[30,2342,2343],{},"f(int (*)(int));\n",[30,2345,2346],{"class":32,"line":58},[30,2347,2348],{},"fwd(Foo); \u002F\u002F 无法判断选择哪个Foo\n",[20,2350,2352],{"className":22,"code":2351,"language":24,"meta":25,"style":25},"template\u003Ctypename T>\nT Foo(T param) {...}\nfwd(Foo) \u002F\u002F 无法判断那个Foo\n",[27,2353,2354,2358,2363],{"__ignoreMap":25},[30,2355,2356],{"class":32,"line":33},[30,2357,36],{},[30,2359,2360],{"class":32,"line":39},[30,2361,2362],{},"T Foo(T param) {...}\n",[30,2364,2365],{"class":32,"line":45},[30,2366,2367],{},"fwd(Foo) \u002F\u002F 无法判断那个Foo\n",[63,2369,2370],{"id":2370},"位域",[10,2372,2374],{"id":2373},"chapter-6-lambda表达式","CHAPTER 6 Lambda表达式",[15,2376,2378],{"id":2377},"条款31-避免使用默认捕获模式","条款31 避免使用默认捕获模式",[71,2380,2381],{},"闭包只会对lambda被创建时所在的作用域里的非静态局部变量生效，因此不能捕获成员变量",[20,2383,2385],{"className":22,"code":2384,"language":24,"meta":25,"style":25},"class Widget\n{\nprivate:\n  int m_value;\npublic:\n  void Test()\n  {\n    auto f = [=]() {return m_value;};\n  }\n};\n",[27,2386,2387,2391,2395,2399,2404,2408,2413,2418,2423,2427],{"__ignoreMap":25},[30,2388,2389],{"class":32,"line":33},[30,2390,675],{},[30,2392,2393],{"class":32,"line":39},[30,2394,348],{},[30,2396,2397],{"class":32,"line":45},[30,2398,1327],{},[30,2400,2401],{"class":32,"line":51},[30,2402,2403],{},"  int m_value;\n",[30,2405,2406],{"class":32,"line":58},[30,2407,684],{},[30,2409,2410],{"class":32,"line":115},[30,2411,2412],{},"  void Test()\n",[30,2414,2415],{"class":32,"line":121},[30,2416,2417],{},"  {\n",[30,2419,2420],{"class":32,"line":126},[30,2421,2422],{},"    auto f = [=]() {return m_value;};\n",[30,2424,2425],{"class":32,"line":132},[30,2426,601],{},[30,2428,2429],{"class":32,"line":138},[30,2430,699],{},[71,2432,2433,2434,2437,2438],{},"在成员函数类，相当于隐式捕获this，lambda中的",[27,2435,2436],{},"m_value","是",[27,2439,2440],{},"this->m_value",[20,2442,2444],{"className":22,"code":2443,"language":24,"meta":25,"style":25},"void Widget::Test()\n{\n  auto current_object_ptr = this;\n  auto f = [current_object_ptr](){return current_object_ptr->m_value;};\n}\n",[27,2445,2446,2451,2455,2460,2465],{"__ignoreMap":25},[30,2447,2448],{"class":32,"line":33},[30,2449,2450],{},"void Widget::Test()\n",[30,2452,2453],{"class":32,"line":39},[30,2454,348],{},[30,2456,2457],{"class":32,"line":45},[30,2458,2459],{},"  auto current_object_ptr = this;\n",[30,2461,2462],{"class":32,"line":51},[30,2463,2464],{},"  auto f = [current_object_ptr](){return current_object_ptr->m_value;};\n",[30,2466,2467],{"class":32,"line":58},[30,2468,358],{},[71,2470,2471],{},"定义在全局空间或者指定命名空间的全局变量，或者是一个声明为static的类内或文件内的成员。\n这些对象也能在lambda中使用，但它们不能被捕获",[20,2473,2475],{"className":22,"code":2474,"language":24,"meta":25,"style":25},"static int a = 1;\nauto f = [=](){return a;};\n++a;\n",[27,2476,2477,2482,2487],{"__ignoreMap":25},[30,2478,2479],{"class":32,"line":33},[30,2480,2481],{},"static int a = 1;\n",[30,2483,2484],{"class":32,"line":39},[30,2485,2486],{},"auto f = [=](){return a;};\n",[30,2488,2489],{"class":32,"line":45},[30,2490,2491],{},"++a;\n",[71,2493,2494],{},"虽然按值捕获，但并不能捕获a，在调用f()时，返回的是++a后的值，相当于是按引用捕获。\n因此，在开始时就应该避免使用默认的按值捕获模式，以免误解",[15,2496,2498],{"id":2497},"条款32-使用初始化捕获来移动对象到闭包中","条款32 使用初始化捕获来移动对象到闭包中",[71,2500,2501],{},"C++ 14中可使用初始化捕获",[20,2503,2505],{"className":22,"code":2504,"language":24,"meta":25,"style":25},"class Widget；\nautp pw = std::make_unique\u003CWidget>();\nauto func = [pw = std::move(pw)] {...};\n",[27,2506,2507,2512,2517],{"__ignoreMap":25},[30,2508,2509],{"class":32,"line":33},[30,2510,2511],{},"class Widget；\n",[30,2513,2514],{"class":32,"line":39},[30,2515,2516],{},"autp pw = std::make_unique\u003CWidget>();\n",[30,2518,2519],{"class":32,"line":45},[30,2520,2521],{},"auto func = [pw = std::move(pw)] {...};\n",[71,2523,2524],{},"C++ 11中可替代方法",[147,2526,2527,2593],{},[78,2528,2529,2530],{},"闭包类",[20,2531,2533],{"className":22,"code":2532,"language":24,"meta":25,"style":25},"class Entity\n{\npublic:\n  explicit Entity(std::unique_ptr\u003CWidget>&& ptr) : pw(std::move(ptr)) {}\n  bool operator()() const\n  {\n    ...\n  }\nprivate:\n  std::unique_ptr pw;\n};\n\nauto func = Entity(std::make_unique\u003CWidget>());\n",[27,2534,2535,2540,2544,2548,2553,2558,2562,2567,2571,2575,2580,2584,2588],{"__ignoreMap":25},[30,2536,2537],{"class":32,"line":33},[30,2538,2539],{},"class Entity\n",[30,2541,2542],{"class":32,"line":39},[30,2543,348],{},[30,2545,2546],{"class":32,"line":45},[30,2547,684],{},[30,2549,2550],{"class":32,"line":51},[30,2551,2552],{},"  explicit Entity(std::unique_ptr\u003CWidget>&& ptr) : pw(std::move(ptr)) {}\n",[30,2554,2555],{"class":32,"line":58},[30,2556,2557],{},"  bool operator()() const\n",[30,2559,2560],{"class":32,"line":115},[30,2561,2417],{},[30,2563,2564],{"class":32,"line":121},[30,2565,2566],{},"    ...\n",[30,2568,2569],{"class":32,"line":126},[30,2570,601],{},[30,2572,2573],{"class":32,"line":132},[30,2574,1327],{},[30,2576,2577],{"class":32,"line":138},[30,2578,2579],{},"  std::unique_ptr pw;\n",[30,2581,2582],{"class":32,"line":212},[30,2583,699],{},[30,2585,2586],{"class":32,"line":797},[30,2587,55],{"emptyLinePlaceholder":54},[30,2589,2590],{"class":32,"line":803},[30,2591,2592],{},"auto func = Entity(std::make_unique\u003CWidget>());\n",[78,2594,2595,2596],{},"std::bind",[20,2597,2599],{"className":22,"code":2598,"language":24,"meta":25,"style":25},"using UniquePtr = std::unique_ptr\u003CWidget>;\nUniquePtr pw = std::make_unique\u003CWidget>();\nauto func = std::bind([](const UniquePtr& ptr){...}, pw);\n",[27,2600,2601,2606,2611],{"__ignoreMap":25},[30,2602,2603],{"class":32,"line":33},[30,2604,2605],{},"using UniquePtr = std::unique_ptr\u003CWidget>;\n",[30,2607,2608],{"class":32,"line":39},[30,2609,2610],{},"UniquePtr pw = std::make_unique\u003CWidget>();\n",[30,2612,2613],{"class":32,"line":45},[30,2614,2615],{},"auto func = std::bind([](const UniquePtr& ptr){...}, pw);\n",[15,2617,2619],{"id":2618},"条款33-对于stdforward的auto形参使用decltype","条款33 对于std::forward的auto&&形参使用decltype",[20,2621,2623],{"className":22,"code":2622,"language":24,"meta":25,"style":25},"auto f =\n  [](auto&&... params)\n  {\n    return func(normailized(std::forward\u003Cdecltype(params)>(params)...));\n  }\n",[27,2624,2625,2630,2635,2639,2644],{"__ignoreMap":25},[30,2626,2627],{"class":32,"line":33},[30,2628,2629],{},"auto f =\n",[30,2631,2632],{"class":32,"line":39},[30,2633,2634],{},"  [](auto&&... params)\n",[30,2636,2637],{"class":32,"line":45},[30,2638,2417],{},[30,2640,2641],{"class":32,"line":51},[30,2642,2643],{},"    return func(normailized(std::forward\u003Cdecltype(params)>(params)...));\n",[30,2645,2646],{"class":32,"line":58},[30,2647,601],{},[15,2649,2651],{"id":2650},"条款34-考虑lambda表达式而非stdbind","条款34 考虑lambda表达式而非std::bind",[147,2653,2654,2657],{},[78,2655,2656],{},"与使用std::bind相比，Lambda更易读，更具有表达力并且可能更高效",[78,2658,2659],{},"只有在C++11中，std::bind可能对实现移动捕获或使用模板化函数调用运算符来绑定对象时会很有用",[10,2661,2663],{"id":2662},"chapter-7-并发api","CHAPTER 7 并发API",[15,2665,2667],{"id":2666},"条款35-优先基于任务编程而不是基于线程","条款35 优先基于任务编程而不是基于线程",[147,2669,2670,2676,2679],{},[78,2671,2672,2675],{},[27,2673,2674],{},"std::thread","不能直接访问异步执行的结果，如果执行函数有异常抛出，代码会终止执行",[78,2677,2678],{},"基于线程的编程方式关于解决资源超限，负载均衡的方案移植性不佳",[78,2680,2681,2684],{},[27,2682,2683],{},"std::async","会默认解决上面问题",[15,2686,2688],{"id":2687},"条款36-确保在异步为必须时才指定stdlaunchasync","条款36 确保在异步为必须时，才指定std::launch::async",[71,2690,1156,2691,2694,2695,2698],{},[27,2692,2693],{},"wait_for()"," or ",[27,2696,2697],{},"wait_until()","时考虑deferred状态",[20,2700,2702],{"className":22,"code":2701,"language":24,"meta":25,"style":25},"void Foo();\nauto f = std::async(Foo);\n\u002F\u002F 当std::async使用std::launch::deferred时，会死循环\nwhile(f.wait_for(1s) != std::future_status::ready) {\n  \u002F\u002F ...\n}\n",[27,2703,2704,2709,2714,2719,2724,2729],{"__ignoreMap":25},[30,2705,2706],{"class":32,"line":33},[30,2707,2708],{},"void Foo();\n",[30,2710,2711],{"class":32,"line":39},[30,2712,2713],{},"auto f = std::async(Foo);\n",[30,2715,2716],{"class":32,"line":45},[30,2717,2718],{},"\u002F\u002F 当std::async使用std::launch::deferred时，会死循环\n",[30,2720,2721],{"class":32,"line":51},[30,2722,2723],{},"while(f.wait_for(1s) != std::future_status::ready) {\n",[30,2725,2726],{"class":32,"line":58},[30,2727,2728],{},"  \u002F\u002F ...\n",[30,2730,2731],{"class":32,"line":115},[30,2732,358],{},[71,2734,2735],{},"可以先判断一下是否为deferred",[20,2737,2739],{"className":22,"code":2738,"language":24,"meta":25,"style":25},"if(f.wait_for(0s) == std::future_status::deferred) {\n  \u002F\u002F ...\n} else {\n while(f.wait_for(1s) != std::future_status::ready) {\n  \u002F\u002F ...\n }\n}\n",[27,2740,2741,2746,2750,2755,2760,2764,2769],{"__ignoreMap":25},[30,2742,2743],{"class":32,"line":33},[30,2744,2745],{},"if(f.wait_for(0s) == std::future_status::deferred) {\n",[30,2747,2748],{"class":32,"line":39},[30,2749,2728],{},[30,2751,2752],{"class":32,"line":45},[30,2753,2754],{},"} else {\n",[30,2756,2757],{"class":32,"line":51},[30,2758,2759],{}," while(f.wait_for(1s) != std::future_status::ready) {\n",[30,2761,2762],{"class":32,"line":58},[30,2763,2728],{},[30,2765,2766],{"class":32,"line":115},[30,2767,2768],{}," }\n",[30,2770,2771],{"class":32,"line":121},[30,2772,358],{},[15,2774,2776],{"id":2775},"条款37-从各个方面使得stdthreads-unjoinable","条款37: 从各个方面使得std::threads unjoinable",[20,2778,2780],{"className":22,"code":2779,"language":24,"meta":25,"style":25},"class ThreadRAII {\npublic:\n  enum class DtorAction{ join, detch };\n  ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}\n  ~ThreadRAII() {\n    if(t.joinable()) {\n      if(action == DtorAction::join) {\n        t.join();\n      } else {\n        t.detach();\n      }\n    }\n  }\n\n  ThreadRAII(ThreadRAII&&) = default;\n  ThreadRAII& operator=(ThreadRAII&&) = default;\n  std::thread& get() { return t; }\nprivate:\n  DtorAction action;\n  std::thread t;\n};\n",[27,2781,2782,2787,2791,2796,2801,2806,2811,2816,2821,2826,2831,2836,2841,2845,2849,2854,2859,2864,2868,2874,2880],{"__ignoreMap":25},[30,2783,2784],{"class":32,"line":33},[30,2785,2786],{},"class ThreadRAII {\n",[30,2788,2789],{"class":32,"line":39},[30,2790,684],{},[30,2792,2793],{"class":32,"line":45},[30,2794,2795],{},"  enum class DtorAction{ join, detch };\n",[30,2797,2798],{"class":32,"line":51},[30,2799,2800],{},"  ThreadRAII(std::thread&& t, DtorAction a): action(a), t(std::move(t)) {}\n",[30,2802,2803],{"class":32,"line":58},[30,2804,2805],{},"  ~ThreadRAII() {\n",[30,2807,2808],{"class":32,"line":115},[30,2809,2810],{},"    if(t.joinable()) {\n",[30,2812,2813],{"class":32,"line":121},[30,2814,2815],{},"      if(action == DtorAction::join) {\n",[30,2817,2818],{"class":32,"line":126},[30,2819,2820],{},"        t.join();\n",[30,2822,2823],{"class":32,"line":132},[30,2824,2825],{},"      } else {\n",[30,2827,2828],{"class":32,"line":138},[30,2829,2830],{},"        t.detach();\n",[30,2832,2833],{"class":32,"line":212},[30,2834,2835],{},"      }\n",[30,2837,2838],{"class":32,"line":797},[30,2839,2840],{},"    }\n",[30,2842,2843],{"class":32,"line":803},[30,2844,601],{},[30,2846,2847],{"class":32,"line":1324},[30,2848,55],{"emptyLinePlaceholder":54},[30,2850,2851],{"class":32,"line":1330},[30,2852,2853],{},"  ThreadRAII(ThreadRAII&&) = default;\n",[30,2855,2856],{"class":32,"line":1336},[30,2857,2858],{},"  ThreadRAII& operator=(ThreadRAII&&) = default;\n",[30,2860,2861],{"class":32,"line":1342},[30,2862,2863],{},"  std::thread& get() { return t; }\n",[30,2865,2866],{"class":32,"line":1431},[30,2867,1327],{},[30,2869,2871],{"class":32,"line":2870},19,[30,2872,2873],{},"  DtorAction action;\n",[30,2875,2877],{"class":32,"line":2876},20,[30,2878,2879],{},"  std::thread t;\n",[30,2881,2883],{"class":32,"line":2882},21,[30,2884,699],{},[71,2886,2887,2888,2891,2892,2895,2896,2899],{},"析构时执行",[27,2889,2890],{},"join()","可能导致性能异常，执行",[27,2893,2894],{},"detach()","可能导致bug(",[1476,2897,2898],{},"详情见原书",")，\n适当的解决方案是中断线程，详情见《C++ Concurrency in Action》9.2部分",[15,2901,2903],{"id":2902},"条款38-关注不同线程句柄析构行为","条款38: 关注不同线程句柄析构行为",[68,2905,2906],{},[71,2907,2908,2909,2912],{},"promise搭配future使用时，结果存储在",[1476,2910,2911],{},"共享状态","*(shared state)中，而共享状态通常\n是基于堆的对象",[147,2914,2915,2924],{},[78,2916,2917,2920,2921,2923],{},[27,2918,2919],{},"future","的正常析构行为就是销毁",[27,2922,2919],{},"本身的成员数据",[78,2925,2926,2927,2929,2930,2932],{},"最后一个引用",[27,2928,2683],{},"创建共享状态的",[27,2931,2919],{},"析构函数会在任务结束前block",[15,2934,2936],{"id":2935},"条款39-对于一次性事件通讯考虑使用无返回-future","条款39: 对于一次性事件通讯考虑使用无返回 future",[71,2938,2939],{},"一个任务通知另一个异步任务执行的方法",[147,2941,2942,2960,3020],{},[78,2943,2944,2945,2944,2947,2950,2951],{},"条件变量",[225,2946],{},[27,2948,2949],{},"wait()","语句存在虚假唤醒，即使条件变量没有被通知，也可能被唤醒，\n可使用Lambda解决",[20,2952,2954],{"className":22,"code":2953,"language":24,"meta":25,"style":25},"cv.wait(lk, []{return whether the event has occurred; });\n",[27,2955,2956],{"__ignoreMap":25},[30,2957,2958],{"class":32,"line":33},[30,2959,2953],{},[78,2961,2962,2963,3013,3015,3016,3019],{},"共享的boolean标志",[20,2964,2966],{"className":22,"code":2965,"language":24,"meta":25,"style":25},"void Foo1() {\n  std::atomic\u003Cbool> flag(false);\n  \u002F\u002F ...\n  flag = true;\n}\nvoid Foo2() {\n  \u002F\u002F ...\n  while(!false);\n  \u002F\u002F ...\n}\n",[27,2967,2968,2973,2978,2982,2987,2991,2996,3000,3005,3009],{"__ignoreMap":25},[30,2969,2970],{"class":32,"line":33},[30,2971,2972],{},"void Foo1() {\n",[30,2974,2975],{"class":32,"line":39},[30,2976,2977],{},"  std::atomic\u003Cbool> flag(false);\n",[30,2979,2980],{"class":32,"line":45},[30,2981,2728],{},[30,2983,2984],{"class":32,"line":51},[30,2985,2986],{},"  flag = true;\n",[30,2988,2989],{"class":32,"line":58},[30,2990,358],{},[30,2992,2993],{"class":32,"line":115},[30,2994,2995],{},"void Foo2() {\n",[30,2997,2998],{"class":32,"line":121},[30,2999,2728],{},[30,3001,3002],{"class":32,"line":126},[30,3003,3004],{},"  while(!false);\n",[30,3006,3007],{"class":32,"line":132},[30,3008,2728],{},[30,3010,3011],{"class":32,"line":138},[30,3012,358],{},[225,3014],{},"无锁以及没有虚假唤醒，但是",[27,3017,3018],{},"Foo2()","会一直占用CPU",[78,3021,3022,3023,3079,3081,3082,3085,3086,3089,3090,3092,3093,3095,3096,3098],{},"promise与future",[20,3024,3026],{"className":22,"code":3025,"language":24,"meta":25,"style":25},"std::promise\u003Cvoid> p;\n\nvoid Foo1() {\n  \u002F\u002F ...\n  p.set_value();\n}\n\nvoid Foo2() {\n  \u002F\u002F ...\n  p.get_future().wait();\n  \u002F\u002F ...\n}\n",[27,3027,3028,3033,3037,3041,3045,3050,3054,3058,3062,3066,3071,3075],{"__ignoreMap":25},[30,3029,3030],{"class":32,"line":33},[30,3031,3032],{},"std::promise\u003Cvoid> p;\n",[30,3034,3035],{"class":32,"line":39},[30,3036,55],{"emptyLinePlaceholder":54},[30,3038,3039],{"class":32,"line":45},[30,3040,2972],{},[30,3042,3043],{"class":32,"line":51},[30,3044,2728],{},[30,3046,3047],{"class":32,"line":58},[30,3048,3049],{},"  p.set_value();\n",[30,3051,3052],{"class":32,"line":115},[30,3053,358],{},[30,3055,3056],{"class":32,"line":121},[30,3057,55],{"emptyLinePlaceholder":54},[30,3059,3060],{"class":32,"line":126},[30,3061,2995],{},[30,3063,3064],{"class":32,"line":132},[30,3065,2728],{},[30,3067,3068],{"class":32,"line":138},[30,3069,3070],{},"  p.get_future().wait();\n",[30,3072,3073],{"class":32,"line":212},[30,3074,2728],{},[30,3076,3077],{"class":32,"line":797},[30,3078,358],{},[225,3080],{},"无锁，没有虚假唤醒，不会一直占用CPU，但",[27,3083,3084],{},"std::promise","与",[27,3087,3088],{},"std::future","间有共享\n状态，并且共享状态是动态分配的，会有分配与释放的开销",[225,3091],{},"同时，",[27,3094,3084],{},"只能设置一次，即与",[27,3097,3088],{},"之间的通信是一次性的",[15,3100,3102],{"id":3101},"条款40-对于并发请使用stdatomicvolatile用于特殊内存区","条款40: 对于并发请使用std::atomic、volatile用于特殊内存区",[147,3104,3105,3111],{},[78,3106,3107,3110],{},[27,3108,3109],{},"std::atomic","用在并发程序中",[78,3112,3113,3116],{},[27,3114,3115],{},"volatile","用于特殊内存的场景中，避免被编译器优化内存",[10,3118,3120],{"id":3119},"chapter-8-微调","CHAPTER 8 微调",[15,3122,3124],{"id":3123},"条款41-如果参数可拷贝并且移动操作开销很低总是考虑直接按值传递","条款41 如果参数可拷贝并且移动操作开销很低，总是考虑直接按值传递",[20,3126,3128],{"className":22,"code":3127,"language":24,"meta":25,"style":25},"std::vector\u003Cstd::string> names;\n\n\u002F\u002F 1.重载 左值会由一次拷贝，右值由一次移动\nvoid AddName(const std::string& newName)\n{\n  names.push_back(newName);\n}\nvoid AddName(std::string&& newName)\n{\n  names.push_back(std::move(newName));\n}\n\n\u002F\u002F 2.通用模板 与重载开销相同\ntemplate\u003Ctypename T>\nvoid AddName(T&& newName)\n{\n  names.push_back(std::forward\u003CT>(newName));\n}\n\n\n\u002F\u002F 3.按值传递 左值参数，一次拷贝一次移动，右值参数，两次拷贝\nvoid AddName(std::string newName)\n{\n  names.push_back(std::move(newName));\n}\n",[27,3129,3130,3135,3139,3144,3149,3153,3158,3162,3167,3171,3176,3180,3184,3189,3193,3198,3202,3207,3211,3215,3219,3224,3230,3235,3240],{"__ignoreMap":25},[30,3131,3132],{"class":32,"line":33},[30,3133,3134],{},"std::vector\u003Cstd::string> names;\n",[30,3136,3137],{"class":32,"line":39},[30,3138,55],{"emptyLinePlaceholder":54},[30,3140,3141],{"class":32,"line":45},[30,3142,3143],{},"\u002F\u002F 1.重载 左值会由一次拷贝，右值由一次移动\n",[30,3145,3146],{"class":32,"line":51},[30,3147,3148],{},"void AddName(const std::string& newName)\n",[30,3150,3151],{"class":32,"line":58},[30,3152,348],{},[30,3154,3155],{"class":32,"line":115},[30,3156,3157],{},"  names.push_back(newName);\n",[30,3159,3160],{"class":32,"line":121},[30,3161,358],{},[30,3163,3164],{"class":32,"line":126},[30,3165,3166],{},"void AddName(std::string&& newName)\n",[30,3168,3169],{"class":32,"line":132},[30,3170,348],{},[30,3172,3173],{"class":32,"line":138},[30,3174,3175],{},"  names.push_back(std::move(newName));\n",[30,3177,3178],{"class":32,"line":212},[30,3179,358],{},[30,3181,3182],{"class":32,"line":797},[30,3183,55],{"emptyLinePlaceholder":54},[30,3185,3186],{"class":32,"line":803},[30,3187,3188],{},"\u002F\u002F 2.通用模板 与重载开销相同\n",[30,3190,3191],{"class":32,"line":1324},[30,3192,36],{},[30,3194,3195],{"class":32,"line":1330},[30,3196,3197],{},"void AddName(T&& newName)\n",[30,3199,3200],{"class":32,"line":1336},[30,3201,348],{},[30,3203,3204],{"class":32,"line":1342},[30,3205,3206],{},"  names.push_back(std::forward\u003CT>(newName));\n",[30,3208,3209],{"class":32,"line":1431},[30,3210,358],{},[30,3212,3213],{"class":32,"line":2870},[30,3214,55],{"emptyLinePlaceholder":54},[30,3216,3217],{"class":32,"line":2876},[30,3218,55],{"emptyLinePlaceholder":54},[30,3220,3221],{"class":32,"line":2882},[30,3222,3223],{},"\u002F\u002F 3.按值传递 左值参数，一次拷贝一次移动，右值参数，两次拷贝\n",[30,3225,3227],{"class":32,"line":3226},22,[30,3228,3229],{},"void AddName(std::string newName)\n",[30,3231,3233],{"class":32,"line":3232},23,[30,3234,348],{},[30,3236,3238],{"class":32,"line":3237},24,[30,3239,3175],{},[30,3241,3243],{"class":32,"line":3242},25,[30,3244,358],{},[71,3246,3247],{},[936,3248,3249],{},"后面没看懂。。。",[147,3251,3252,3255,3258],{},[78,3253,3254],{},"对于可复制，移动开销低，而且无条件复制的参数，按值传递效率基本与按引用传递效率一致，而且\n易于实现，生成更少的目标代码",[78,3256,3257],{},"通过拷贝构造拷贝参数，可能比通过赋值拷贝开销大的多",[78,3259,3260],{},"按值转递会引起切片问题，所说不适合基类类型的参数",[15,3262,3264],{"id":3263},"条款42-考虑使用emplacement代替insertion","条款42 考虑使用emplacement代替insertion",[71,3266,3267],{},[936,3268,3269],{},"也没怎么看懂",[147,3271,3272,3275,3289],{},[78,3273,3274],{},"原则上，emplacement函数有时会比insertion函数高效，并且不会更差",[78,3276,3277,3278],{},"实际上，当执行如下操作时，emplacement函数更快",[75,3279,3280,3283,3286],{},[78,3281,3282],{},"值被构造到容器中，而不是直接赋值",[78,3284,3285],{},"传入的类型与容器类型不一致",[78,3287,3288],{},"容器不拒绝已经存在的重复值",[78,3290,3291],{},"emplacement函数可能执行insertion函数拒绝的显示构造",[3293,3294,3295],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":25,"searchDepth":39,"depth":39,"links":3297},[3298,3304,3308,3321,3328,3338,3344,3352],{"id":12,"depth":39,"text":13,"children":3299},[3300,3301,3302,3303],{"id":17,"depth":45,"text":18},{"id":427,"depth":45,"text":428},{"id":496,"depth":45,"text":497},{"id":539,"depth":45,"text":540},{"id":543,"depth":39,"text":544,"children":3305},[3306,3307],{"id":547,"depth":45,"text":548},{"id":551,"depth":45,"text":552},{"id":644,"depth":39,"text":645,"children":3309},[3310,3311,3312,3313,3314,3315,3316,3317,3318,3319,3320],{"id":648,"depth":45,"text":649},{"id":728,"depth":45,"text":729},{"id":732,"depth":45,"text":733},{"id":834,"depth":45,"text":835},{"id":838,"depth":45,"text":839},{"id":850,"depth":45,"text":851},{"id":854,"depth":45,"text":855},{"id":858,"depth":45,"text":859},{"id":862,"depth":45,"text":863},{"id":902,"depth":45,"text":903},{"id":906,"depth":45,"text":907},{"id":910,"depth":39,"text":911,"children":3322},[3323,3324,3325,3326,3327],{"id":914,"depth":45,"text":915},{"id":951,"depth":45,"text":952},{"id":1094,"depth":45,"text":1095},{"id":1098,"depth":45,"text":1099},{"id":1252,"depth":45,"text":1253},{"id":1469,"depth":39,"text":1470,"children":3329},[3330,3331,3332,3333,3334,3335,3336,3337],{"id":1486,"depth":45,"text":1487},{"id":1604,"depth":45,"text":1605},{"id":1660,"depth":45,"text":1661},{"id":1835,"depth":45,"text":1836},{"id":1955,"depth":45,"text":1956},{"id":2178,"depth":45,"text":2179},{"id":2182,"depth":45,"text":2183},{"id":2186,"depth":45,"text":2187},{"id":2373,"depth":39,"text":2374,"children":3339},[3340,3341,3342,3343],{"id":2377,"depth":45,"text":2378},{"id":2497,"depth":45,"text":2498},{"id":2618,"depth":45,"text":2619},{"id":2650,"depth":45,"text":2651},{"id":2662,"depth":39,"text":2663,"children":3345},[3346,3347,3348,3349,3350,3351],{"id":2666,"depth":45,"text":2667},{"id":2687,"depth":45,"text":2688},{"id":2775,"depth":45,"text":2776},{"id":2902,"depth":45,"text":2903},{"id":2935,"depth":45,"text":2936},{"id":3101,"depth":45,"text":3102},{"id":3119,"depth":39,"text":3120,"children":3353},[3354,3355],{"id":3123,"depth":45,"text":3124},{"id":3263,"depth":45,"text":3264},"2023-06-18","笔记","md",{},"\u002Fblog\u002Feffective-modern-cplusplus",{"title":5,"description":3357},"blog\u002Feffective-modern-cplusplus",[3364],"C++","ADN43sXTlznATJOFVLq0JYFUzN-Uw74bs7lkPz59rwQ",{"id":3367,"title":3368,"avatar":3369,"body":3370,"description":3374,"extension":3358,"meta":3375,"name":3377,"navigation":54,"path":3378,"seo":3379,"social":3380,"stem":3386,"__hash__":3387},"home\u002Fhome.md","Home","https:\u002F\u002Fstarrobe-blog.oss-cn-beijing.aliyuncs.com\u002Favatar\u002Fjashinchan.jpg",{"type":7,"value":3371,"toc":3372},[],{"title":25,"searchDepth":39,"depth":39,"links":3373},[],"Programming enthusiast, maybe.",{"layout":3376},"page","阿东","\u002Fhome",{"title":3368,"description":3374},{"github":3381,"email":3382,"bilibili":3383,"qq":3384,"rss":3385},"https:\u002F\u002Fgithub.com\u002Fstarrobe","mailto:starrobe@163.com","https:\u002F\u002Fspace.bilibili.com\u002F382631863","tencent:\u002F\u002Fmessage\u002F?uin=2604335528","\u002Ffeed.xml","home","XHOrpn2fXb8x87SMsqZTGaANCESyH9qV8TDm8rnQewI",1777128585857]