diff --git a/README.md b/README.md
index 86e9ae5..f29351b 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ RSS使用仓库的release RSS [链接](https://github.com/wanghenshui/cppweeklyn
## 2024
-| [171](./posts/171.md) | | | | | | | | | |
+| [171](./posts/171.md) | [172](./posts/172.md) | | | | | | | | |
| ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | ------------------ |
| [161](./posts/161.md) | [162](./posts/162.md) | [163](./posts/163.md) | [164](./posts/164.md) | [165](./posts/165.md) | [166](./posts/166.md) | [167](./posts/167.md) | [168](./posts/168.md) | [169](./posts/169.md) | [170](./posts/170.md) |
| [151](./posts/151.md) | [152](./posts/152.md) | [153](./posts/153.md) | [154](./posts/154.md) | [155](./posts/155.md) | [156](./posts/156.md) | [157](./posts/157.md) | [158](./posts/158.md) | [159](./posts/159.md) | [160](./posts/160.md) |
diff --git a/posts/172.md b/posts/172.md
new file mode 100644
index 0000000..8ee14c5
--- /dev/null
+++ b/posts/172.md
@@ -0,0 +1,559 @@
+---
+layout: post
+title: 第172期
+---
+# C++ 中文周刊 2024-11-16 第172期
+
+
+[周刊项目地址](https://github.com/wanghenshui/cppweeklynews)
+
+公众号
+
+
+
+点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
+
+qq群 [点击进入](https://qm.qq.com/q/6NGizNPyG4) 满了加这俩 729240657 866408334
+
+[RSS](https://github.com/wanghenshui/cppweeklynews/releases.atom)
+
+欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
+
+本期文章由 HNY 赞助 在此表示感谢
+
+---
+
+## 资讯
+
+标准委员会动态/ide/编译器信息放在这里
+
+[编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-11-13 第280期 ](https://mp.weixin.qq.com/s/4XWPWIaQ22t1V4UuP0qxWA)
+
+[clang增加了一个safebuffer模式 ](https://clang.llvm.org/docs/SafeBuffers.html)
+
+可以使用 `-Wunsafe-buffer-usage` 目前还在开发中
+
+[clang增加了函数分析能力 ](https://clang.llvm.org/docs/FunctionEffectAnalysis.html)
+
+在noexcept 基础上增加了noblocking noallocating
+
+更准确分析函数行为,可以配合之前介绍的RealTimeSan使用
+
+主要问题是函数指针不行,函数指针/function自动丢弃上述属性
+
+另外存在属性覆盖,noblocking noallocating的必要条件是noexcept,权限大于,大家懂我意思吧
+
+函数指针怎么绕过
+
+```C++
+std::sort(vec.begin(), vec.end(),
+ [](const Elem& a, const Elem& b) [[clang::nonblocking]] { return a.mem < b.mem; }); // OK
+
+static bool compare_elems(const Elem& a, const Elem& b) [[clang::nonblocking]] {
+ return a.mem < b.mem; }; // 不行 属性会丢
+
+std::sort(vec.begin(), vec.end(), compare_elems);
+
+template
+class nonblocking_fp;
+
+template
+class nonblocking_fp {
+public:
+ using impl_t = R (*)(Args...) [[clang::nonblocking]];
+
+private:
+ impl_t mImpl{ nullptr_t };
+public:
+ nonblocking_fp() = default;
+ nonblocking_fp(impl_t f) : mImpl{ f } {}
+
+ R operator()(Args... args) const
+ {
+ return mImpl(std::forward(args)...);
+ }
+};
+
+// deduction guide (like std::function's)
+template< class R, class... ArgTypes >
+nonblocking_fp( R(*)(ArgTypes...) ) -> nonblocking_fp;
+
+// --
+
+// Wrap the function pointer in a functor which preserves ``nonblocking``.
+std::sort(vec.begin(), vec.end(), nonblocking_fp{ compare_elems });
+```
+
+## 文章
+
+- [Use std::span instead of C-style arrays ](https://www.sandordargo.com/blog/2024/11/06/std-span)
+
+~~我用得着你说?~~强调span不容易用错
+
+
+- [复习一下c++98 SFINAE](https://jguegant.github.io/blogs/tech/sfinae-introduction.html)
+
+```C++
+template
+class HasIsNullMethod {
+ struct Yes { char unused[1]; };
+ struct No { char unused[2]; };
+ template struct reallyHas;
+ template static Yes& test(reallyHas* /*unused*/) { }
+ template static Yes& test(reallyHas* /*unused*/) { }
+ // template static Yes test(char(*)[static_cast(&C::isNull == false) + 1]) {}
+ template static No test(...);
+public:
+ static const bool Value = (sizeof(test(0)) == sizeof(Yes));
+};
+
+struct A {
+ char isNull() {}
+};
+struct B {
+ void isNull() {}
+};
+
+
+struct C {
+ char isNull;
+};
+
+bool fA() {
+ return HasIsNullMethod::Value;
+}
+
+bool fB() {
+ return HasIsNullMethod::Value;
+}
+
+bool fC() {
+ return HasIsNullMethod::Value;
+}
+// Type your code here, or load an example.
+bool fint()
+{
+ return HasIsNullMethod::Value;
+}
+#include
+int main() {
+ std::cout << fint() << "\n";
+ std::cout << fA()<< "\n";
+ std::cout << fB()<< "\n";
+ std::cout << fC()<< "\n";
+ return 0;
+}
+```
+
+[godbolt](https://godbolt.org/z/hTaGja6cW) 不会也没啥,糟粕 喜欢怀旧可以看一下
+
+
+- [unique_ptr and the pointer to implementation idiom](https://andreasfertig.blog/2024/11/unique_ptr-and-the-pointer-to-implementation-idiom/)
+
+
+当实现pimpl惯用法的时候,使用unique_ptr通常因为看不到完整实现(析构)调用失败
+
+作者给的办法是手动加上deleter
+
+不要听他的,直接用shared_ptr,不要多此一举好吧
+
+- [What is the current time around the world? Utilizing std::chrono with time zones in C++23 ](https://www.cppstories.com/2024/chrono_dates_zones/)
+
+
+介绍timezone
+
+我记得有一个date库就做了这个活,用不上可以直接用那个date
+
+简单贴一下代码,介绍一下接口
+
+```C++
+#include
+#include
+
+int main() {
+ const auto now = std::chrono::system_clock::now();
+ auto zt_local = std::chrono::zoned_time{ std::chrono::current_zone(), now };
+ std::print("now is {} UTC and local is: {}\n", now, zt_local);
+
+ constexpr std::string_view Warsaw{ "Europe/Warsaw" };
+ constexpr std::string_view NewYork{ "America/New_York" };
+ constexpr std::string_view Tokyo{ "Asia/Tokyo" };
+
+ try
+ {
+ const std::chrono::zoned_time zt_w{Warsaw, now};
+ std::print("Warsaw: {0:%F} {0:%R}\n", zt_w);
+ const std::chrono::zoned_time zt_ny{NewYork, now};
+ std::print("New York: {0:%F} {0:%R}\n", zt_ny);
+ const std::chrono::zoned_time zt_t{Tokyo, now};
+ std::print("Tokyo: {0:%F} {0:%R}\n", zt_t);
+ }
+ catch (std::runtime_error& ex)
+ {
+ std::print("Error: {}", ex.what());
+ }
+}
+/*
+now is 2024-11-15 22:31:24.193993753 UTC and local is: 2024-11-15 22:31:24.193993753
+Warsaw: 2024-11-15 23:31
+New York: 2024-11-15 17:31
+Tokyo: 2024-11-16 07:31
+*/
+```
+[godbolt](https://godbolt.org/z/rEbfj69qf) 还有其他代码,就不贴了
+
+- [C++, Complexity, and Compiler Bugs](https://azeemba.com/posts/cpp-complexity-compiler-bugs.html)
+
+
+
+这人不懂c++大惊小怪,就是简单的const延长生命周期
+
+~~不看代码了。单纯喷他一下~~
+
+- [Memory error checking in C and C++: Comparing Sanitizers and Valgrind ](https://developers.redhat.com/blog/2021/05/05/memory-error-checking-in-c-and-c-comparing-sanitizers-and-valgrind)
+
+老文章,valgrind不如sanitizer直接/快,且有遗漏
+
+- [UB鉴赏环节](https://pvs-studio.com/en/blog/posts/cpp/1178/)
+
+死循环优化
+
+```C++
+#include
+
+int fermat () {
+ const int MAX = 100;
+ int a=1,b=1,c=1;
+ int iter = 0;
+ while (1) {
+ if ( (a*a*a) == (b*b*b) + (c*c*c) ) {
+ std::cout << "Found!\n";
+ return 1;
+ }
+ a++;
+ if (a>MAX) {
+ a=1;
+ b++;
+ }
+ if (b>MAX) {
+ b=1;
+ c++;
+ }
+ if (c>MAX) {
+ c=1;
+ }
+ ++iter;
+ }
+ return 0;
+}
+
+int main () {
+ if (fermat()) {
+ std::cout << "Fermat's Last Theorem has been disproved.\n";
+ } else {
+ std::cout << "Fermat's Last Theorem has not been disproved.\n";
+ }
+ return 0;
+}
+```
+
+打印
+```txt
+Found!
+Fermat's Last Theorem has been disproved.
+```
+
+由于return是死循环唯一出口,编译器激进到直接return 1
+
+[这个可以看lancern文章有介绍过 ](https://zhuanlan.zhihu.com/p/391088391) 感谢zwuis提醒
+
+
+[析构栈溢出一个例子 ](https://godbolt.org/z/baaqjj39e)
+
+```C++
+#include
+#include
+#include
+
+struct Node {
+ int value = 0;
+ std::vector childrens;
+};
+
+struct List {
+ int value = 0;
+ std::unique_ptr next;
+
+ ~List() {
+ while (next) {
+ // The destructor is still recursive,
+ // but now the recursion depth is 1 call.
+ next = std::move(next->next);
+ }
+ }
+
+ List() noexcept = default;
+ List(List&&) noexcept = default;
+ List& operator=(List&&) noexcept = default;
+};
+
+int main() {
+ List dummynode;
+ List* l = &dummynode;
+
+ int BOUND = 1000;
+ int SUB_BOUND = 100;
+ for (int i = 1; ivalue = i;
+ l->next = std::make_unique();
+ l = l->next.get();
+ }
+ /*
+ // 这个析构会栈溢出
+ Node n;
+ auto tmp = &n;
+ for (int i = 1; ichildrens[j] = c;
+ // tmp = &tmp->childrens[j];
+ }
+ }
+ */
+}
+
+```
+
+noexcept问题 noexcept=noexcept(true), noexcept(cond) 可以自己定制
+
+成员函数除了析构都是noexcept(false) ,如果析构函数抛异常需要显式指明
+
+```C++
+struct SoBad {
+ // invoke std::terminate
+ ~SoBad() {
+ throw std::runtime_error("so bad dtor");
+ }
+};
+
+struct NotSoBad {
+ // OK
+ ~NotSoBad() noexcept(false) {
+ throw std::runtime_error("not so bad dtor");
+ }
+};
+```
+
+使用noexcept需要你写异常验证代码,避免terminate爆炸
+
+缓冲区溢出问题
+
+一大堆傻逼函数 scanf strcpy strcat gets strncat等等,哦还有memcpy
+
+引申出数组越界问题,数组越界会被激进优化,一定要注意
+
+其实有点和前面的例子很相似
+
+```C++
+const int N = 10;
+int elements[N];
+
+bool contains(int x) {
+ for (int i = 0; i <= N; ++i) {
+ if (x == elements[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int main() {
+ for (int i = 0; i < N; ++i) {
+ std::cin >> elements[i];
+ }
+ return contains(5);
+}
+```
+
+存在越界 -> 越界是UB,编译器认为代码中没有UB,说明越界肯定不可达,说明提前返回 所以直接优化成true
+
+
+类似例子
+
+```C++
+const int N = 10;
+int main() {
+ int decade[N];
+ for (int k = 0; k <= N; ++k) {
+ printf("k is %d\n",k);
+ decade[k] = -1;
+ }
+}
+```
+
+k越界,越界是UB,编译器认为代码中没有UB,说明永远到不了N,直接死循环
+
+- [ Analyzing the Performance of the “Proxy” Library ](https://devblogs.microsoft.com/cppblog/analyzing-the-performance-of-the-proxy-library/)
+
+鉴定为不如varint,评论区有人指出性能比varint不太行,测试代码我拿来在7950x WSL跑了一下
+
+```txt
+2024-11-16T23:09:24+08:00
+Running ./benchmarks/msft_proxy_benchmarks
+Run on (32 X 4499.92 MHz CPU s)
+CPU Caches:
+ L1 Data 32 KiB (x16)
+ L1 Instruction 32 KiB (x16)
+ L2 Unified 1024 KiB (x16)
+ L3 Unified 32768 KiB (x1)
+Load Average: 0.65, 0.21, 0.11
+---------------------------------------------------------------------------------------
+Benchmark Time CPU Iterations
+---------------------------------------------------------------------------------------
+BM_SmallObjectInvocationViaProxy 4648790 ns 5071349 ns 136
+BM_SmallObjectInvocationViaVirtualFunction 11986710 ns 13076183 ns 54
+BM_SmallObjectInvocationViaVariant 5044399 ns 5495963 ns 127
+BM_LargeObjectInvocationViaProxy 7689574 ns 8388641 ns 83
+BM_LargeObjectInvocationViaVirtualFunction 9397069 ns 10251350 ns 68
+BM_LargeObjectInvocationViaVariant 5046724 ns 5490317 ns 127
+BM_SmallObjectManagementWithProxy 1603751 ns 1749509 ns 402
+BM_SmallObjectManagementWithUniquePtr 8952997 ns 9766806 ns 72
+BM_SmallObjectManagementWithSharedPtr 11484836 ns 12528993 ns 56
+BM_SmallObjectManagementWithSharedPtr_Pooled 14436982 ns 15749468 ns 44
+BM_SmallObjectManagementWithAny 6424830 ns 7008942 ns 96
+BM_SmallObjectManagementWithVariant 514705 ns 561491 ns 1199
+BM_LargeObjectManagementWithProxy 39497657 ns 43086918 ns 17
+BM_LargeObjectManagementWithProxy_Pooled 30985407 ns 33760433 ns 21
+BM_LargeObjectManagementWithUniquePtr 40788280 ns 44496508 ns 13
+BM_LargeObjectManagementWithSharedPtr 50875412 ns 55500655 ns 11
+BM_LargeObjectManagementWithSharedPtr_Pooled 30422979 ns 33188319 ns 21
+BM_LargeObjectManagementWithAny 32133635 ns 35037580 ns 20
+BM_LargeObjectManagementWithVariant 12639262 ns 13788144 ns 50
+```
+
+这明显不如varint啊。还是观望吧
+
+
+- [How can I explicitly specialize a templated C++ constructor?](https://devblogs.microsoft.com/oldnewthing/20241011-00/?p=110365)
+
+
+如何显式指定构造函数模板形参?怎样让模板构造函数识别不同类型
+
+实际上就是让构造函数作为工厂函数接口,工厂模式大家都熟悉,但是问题在于构造函数不能指定类型
+
+比如这种实例
+
+```C++
+// Assume derived classes by convention have a constructor
+// whose first parameter is an ObjectManager&.
+struct CommonBase
+{
+ virtual ~CommonBase(){}
+ virtual void initialize(int reason) = 0;
+};
+
+struct Widget : CommonBase
+{
+ Widget(int param) {}
+ Widget(Widget&&) {}
+ void initialize(int reason) {
+ std::cout << "ok\n";
+ }
+
+};
+
+struct ObjectManager
+{
+ // Concrete should derive from CommonBase
+ template
+ ObjectManager(int reason,
+ std::in_place_type_t,
+ Args&&...args) :
+ m_base(std::make_unique(
+ std::forward(args)...))
+ {
+ m_base->initialize(reason);
+ }
+ template
+ static ObjectManager make(int reason, Args&&...args)
+ {
+ return ObjectManager(reason,
+ std::in_place_type_t{},
+ std::forward(args)...);
+ }
+ std::unique_ptr m_base;
+};
+
+struct ObjectManager0
+{
+ // Concrete should derive from CommonBase
+ template
+ ObjectManager0(int reason, Args&&...args) :
+ m_base(std::make_unique( std::forward(args)...))
+ {
+ m_base->initialize(reason);
+ }
+
+ std::unique_ptr m_base;
+};
+
+// auto m0 = ObjectManager0(9,42);
+// auto m0 = ObjectManager0::ObjectManager0(9,42);
+auto m2 = ObjectManager::make(9, 42);
+```
+make显然就是工厂函数那种类型构造,但是构造函数没法指定类型
+
+我们怎么给构造函数传递类型?用in_place_type_t
+
+上面的代码还算容易看懂,其实就是tag把类型带过来,有点像identity_type那种玩法
+
+
+不过构造函数模板不如类模板来的直观一些,这么玩有点复杂, 代码 [godbolt](https://godbolt.org/z/qj7YGox9a)
+
+- [The Big Array Size Survey for C](https://thephd.dev/the-big-array-size-survey-for-c)
+
+数组长度命名,纠结老半天,了解历史可以看 ~~(没有看的必要)~~
+
+- [Non-allocating Circular Buffer in C++ ](https://mobiarch.wordpress.com/2024/11/15/non-allocating-circular-buffer-in-c/)
+
+数组 + 循环index,单线程。玩具,[代码 ](https://github.com/bibhas2/CircusBuff/blob/master/circ-buff.h) ~~自己看吧。懒得贴了。不值一看~~
+
+- [Unit Testing Numerical Routines](https://buchanan.one/blog/testing-numerical-algorithms/)
+
+介绍一些代码测试经验,测试数据从何而来,如何更准确的测试接口,如何让测试代码更好的解释函数
+
+感觉做图形学的哥们值得讲一下对应经验
+
+
+## 开源项目介绍
+
+- [asteria](https://github.com/lhmouse/asteria) 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
+- [spdlog 1.15](https://github.com/gabime/spdlog/releases/tag/v1.15.0) 更新,修了一堆bug,没啥重大修复,升不升都行
+
+## 热门库最近更新了什么
+
+[brpc 最近的MR ](https://github.com/apache/brpc/pull/2819)
+
+介绍一下背景
+
+brpc内部是由bthread驱动事件,可以理解为小的线程,类似boost fiber
+
+bthread的调度是wait free的,设计了work steal,如果没有任务就去其他worker线程去偷
+
+问题在于bthread本身是没有优先级的,epoll唤醒和普通IO事件并没有做区分,唤醒的等级应该是最高的
+
+这个MR就是做了个flag区分,让唤醒优先级更高一些
+
+
+## 工作招聘
+
+年底目前没有啥好工作推荐,我在公众微信号会单独发
+
+## 互动环节
+
+本周绝对不鸽好吧
+
+---
+
+[上一期](https://wanghenshui.github.io/cppweeklynews/posts/171.html) [下一期](https://wanghenshui.github.io/cppweeklynews/posts/173.html)