公众号

点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 753792291 答疑在这里
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
本期文章没有赞助 好久没更新了,是在是太懒,空闲时间都用来打街霸6了,终于上了大师, 可以少玩一点了,给家人们更新周刊
家人们点点关注点点赞收藏打赏什么的,给点动力,没动力写不动
不过最近工作,愈发觉得语言更不重要了,AI这几年的进化太夸张了,生产力提升太高,涉及到学语言的地方真的少了
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-01-08 第288期
GCC已经加快更新了反射实现 https://gcc.gnu.org/pipermail/gcc-patches/2025-November/700733.html
重大生产力解放工具,总算来了
另外最近也在开会,没啥新东西
Trip report: November 2025 ISO C++ standards meeting kona
草药老师的纪要
还是contract,没啥意思
介绍了kona会上这个哥们感兴趣的提案
simd针对complex类型打补丁
理解std::constant_arg,以前是std::nontype_t
// libfoo.h:
int foo(std::function_ref<int(float)>);
// app.cpp:
void something() {
// 存储 lambda 对象 -> 间接调用:
foo([](float x) { return int(x * 0.5); });
// 函数指针为 nullptr -> 直接调用:
foo(std::constant_arg<[](float x) { return int(x * 0.5); }>);
}
枚举类型无法隐式转成bool
const auto ret = cpp::some_operation( ... );
if ( ret ) // 编译失败!enum class 无法隐式转换为 bool
if ( ret == cpp::result::Success ) // 可以工作,但更繁琐,我不喜欢打这么多字
能不能外部封装一下?
enum class Result {
Success = 0,
SomeError,
SomeOtherError
};
// 编译失败!不能将 operator bool 定义为静态函数
inline explicit operator bool(Result r) { return r != Result::Success; }
当然AI可能会让你写两个感叹号
inline bool operator!(Result r) { return r == Result::Success; }
void foo()
{
const Result ret = cpp::some_operation( ... );
if ( !!ret )
{
// 处理错误
}
}
太不合理了,我们考虑放到类型内部
struct Result {
enum class Value {
Success = 0,
SomeError,
SomeOtherError
} v;
explicit operator bool() const { return v != Result::Value::Success; }
};
void bar()
{
const Result ret = cpp::some_operation( /* ... */ );
if ( ret.v == Result::Value::SomeError ) {} // 可以工作
if ( ret.v == 42 ) {} // 编译错误
}
还要写那么长的作用域前缀,怎么搞
内部inline一下
struct Result {
enum class Value {
Success = 0,
SomeError,
SomeOtherError
} v;
constexpr Result( Value x ) : v( x ) {}
constexpr explicit operator bool() const { return v != Result::Success; }
static constexpr Value Success = Value::Success;
static constexpr Value SomeError = Value::SomeError;
static constexpr Value SomeOtherError = Value::SomeOtherError;
// 对枚举中的每个值重复此操作
};
inline constexpr bool operator==( Result lhs, Result rhs ) { return lhs.v == rhs.v; }
inline constexpr bool operator!=( Result lhs, Result rhs ) { return lhs.v != rhs.v; }
void foo()
{
const Result ret = cpp::some_operation( /* ... */ );
if ( ret ) {} // 可以工作
if ( ret == Result::SomeError ) {} // 同样可以工作
if ( ret.v == 42 ) {} // 编译错误
}
能用了,现在的问题是,脏活很多,要我说,就AI写就好了
AI看代码AI写代码,甩手掌柜,可能直接两个感叹号爱用不用
简单用c++反射实现相等比较
template<typename T>
bool compare_same(const T& a, const T& b) {
template for (constexpr auto mem : std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()))) {
if (!compare_same(a.[:mem:], b.[:mem:])) {
return false;
}
}
return true;
}
这段代码的原理如下:
完整代码 https://godbolt.org/z/er8b4GbE5
纯右值是值,只有在需要转换的时候才会转成对象
听起来像废话
头文件中的auto会使编译时间增加,可以通过 -ftime-trace 生成 trace文件分析
常规的二叉树写法,包括AI写的,都是双指针类型
class TreeNode {
public:
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int value) : data(value), left(nullptr), right(nullptr) {}
};
局部性很差,作者用数组存索引来改善
class BinaryTreeOptional {
private:
int root = -1;
std::vector<std::optional<TreeNodeOptional>> nodes;
size_t insertRecursive(int node, int value) {
if (node == -1) {
nodes.emplace_back(value);
return nodes.size() - 1;
}
auto node_value = nodes[node]->data;
if (value < node_value) {
nodes[node]->left = insertRecursive(nodes[node]->left, value);
} else if (value > node_value) {
nodes[node]->right = insertRecursive(nodes[node]->right, value);
}
return node;
}
void inOrderRecursive(std::optional<TreeNodeOptional>& onode) {
if (onode.has_value()) {
auto& node = onode.value();
if (node.left != -1)
inOrderRecursive(nodes[node.left]);
if (node.right != -1)
inOrderRecursive(nodes[node.right]);
}
}
public:
void insert(int value) {
root = insertRecursive(root, value);
}
void inOrderTraversal() {
if (root != -1)
inOrderRecursive(nodes[root]);
}
};
局部性好,元数据集中,数据量越多效果越好,性能提升30%+
老代码中有那种遍历map还不好改的代码,如何加速访问,thread_local缓存index
感觉有点难评,能重写最好重写,不要补丁摞补丁
有时候需要RAII+lambda模式,但如果接收方复制了lambda,由于RAII lambda被倒霉的执行两次,如何解决这个问题?
lambda用shared_ptr包一层
这是一个在AArch64 clang上的案例
代码
#include <bit>
int utf8_sequence_length(unsigned char lead_byte) {
switch (std::countl_one(lead_byte)) {
case 0: return 1;
case 2: return 2;
case 3: return 3;
case 4: return 4;
default: return 0; // 无效首字节
}
}
性能不佳,原因是编译器帮忙优化成跳转表了
adrp x9, .Lswitch.table.utf8_sequence_length(unsigned char)
add x9, x9, :lo12:.Lswitch.table.utf8_sequence_length(unsigned char)
ldr w0, [x9, w8, uxtw #2]
...
.Lswitch.table.utf8_sequence_length(unsigned char):
.word 1
.word 0
.word 2
.word 3
.word 4
使用 clang++ -O2 -fno-jump-tables –std=c++20可破
gcc没有生成跳转表,所以没有这个问题
在传统编程中,所有函数都必须返回“一个”值——即使它们可以接受任意数量的参数
这种“多入一出”的结构是不对称的。即使我们将 void 视为“一种返回值”(代表“无值”),也无法改变“返回值数量恒为一”的事实。
函数式语言(如 Haskell)通过柯里化(currying) 将所有函数转化为“一元输入、一元输出”,从而实现对称性。但 C++ 等主流语言缺乏对这种范式的原生支持,更习惯使用多参数函数。
另一种思路是将多个返回值打包成 tuple,但这繁琐且模糊了“返回一个结构”与“返回多个独立值”的语义边界
作者引入一个转换结构
template <callable ...Fs, typename ...Args>
auto call_by_need(some<Fs...>, some<Args...>);
怎么实现,读者拿AI写一个
auto隐藏了类型所有权,隐藏的信息被迫要好好命名,但命名很难
说的有点道理,不过现在AI写代码不auto,我这样的懒狗才auto
是情况而定,尽量不用,名字特长无法忍受可以auto
不过auto IDE也能帮你重构成具体的类型
回顾一下观察者模式咋写,简单说,就是调用路径调用一下外部的钩子
外部钩子可以多个,也就涉及到观察者订阅取消管理。但大部分场景没那么复杂
贴一个代码例子 https://godbolt.org/z/Mv7YMM3aK
#include <iostream>
#include <string_view>
#include <vector>
template <typename Message>
class Subscriber {
public:
virtual ~Subscriber() = default;
virtual void update(Message message) = 0;
};
template <typename Message>
class Publisher {
public:
virtual ~Publisher() = default;
void subscribe(Subscriber<Message>* subscriber) {
std::cout << "Got a new subscriber\n";
_subscribers.push_back(subscriber);
}
void unsubscribe(Subscriber<Message>* subscriber) {
std::cout << "Someone unsubscribed\n";
std::erase(_subscribers, subscriber);
}
protected:
void notify(Message message) const {
std::cout << "Sending an update to " << _subscribers.size()
<< " subscriber(s)\n";
for (auto* const subscriber : _subscribers) {
notifyOne(subscriber, message);
}
}
private:
virtual void notifyOne(Subscriber<Message>* const,
Message message) const = 0;
std::vector<Subscriber<Message>*> _subscribers;
};
using SettingsMessage = std::pair<std::string, int>;
class SettingsSubscriber : public Subscriber<SettingsMessage> {
public:
void update(std::pair<std::string, int> message) override {
std::cout << "Subscriber is getting an update:\n"
<< message.first << "=" << message.second << '\n';
}
};
class SettingsPublisher : public Publisher<SettingsMessage> {
public:
void notifyOne(Subscriber<SettingsMessage>* const subscriber,
SettingsMessage message) const override {
subscriber->update(message);
}
void setSetting1(int value) {
_setting1 = value;
notify({"setting1", _setting1});
}
void setSetting2(int value) {
_setting2 = value;
notify({"setting2", _setting2});
}
private:
int _setting1{0};
int _setting2{0};
};
int main() {
SettingsPublisher pub;
SettingsSubscriber s1, s2;
pub.subscribe(&s1);
pub.subscribe(&s2);
pub.setSetting1(42);
pub.unsubscribe(&s1);
pub.setSetting1(51);
}
/*
Got a new subscriber
Got a new subscriber
Sending an update to 2 subscriber(s)
Subscriber is getting an update:
setting1=42
Subscriber is getting an update:
setting1=42
Someone unsubscribed
Sending an update to 1 subscriber(s)
Subscriber is getting an update:
setting1=51
*/
c++26的改动
支持 maybe_unused
std::pair xy { 42.3, 100.1 };
auto [x, y [[maybe_unused]]] = xy;
std::print("{}", x);
放在条件中
#include <print>
struct Point {
int x { 0 };
int y { 0 };
explicit operator bool() const noexcept { return x > 0; }
};
Point createPoint(int a) { return Point { 10*a, 10*a }; }
int main() {
if (auto [x, y] = createPoint(100))
std::print("point is true");
}
转成一个pack,非常炫酷
auto [head, ...rest] = std::tuple{1,2,3,4};
// head = 1, rest... expands to (2,3,4)
炫酷
// Generic dot product for any tuple-like / array / aggregate supported by structured bindings.
template <class P, class Q>
constexpr auto dot_product(const P& p, const Q& q) {
// Bind all elements of each input into packs.
const auto& [...ps] = p;
const auto& [...qs] = q;
// (Optional) sanity check: both must have the same size
static_assert(sizeof...(ps) == sizeof...(qs), "Mismatched sizes");
// Elementwise multiply, then fold with +
return ((ps * qs) + ...);
}
constexpr std::array<int, 3> a{1, 2, 3};
constexpr std::array<int, 3> b{4, 5, 6};
static_assert(dot_product(a, b) == 32);
支持constexpr
int main() {
constexpr auto [a, b] = std::tuple{2, 3};
static_assert(a * b == 6);
}
看一乐,UCFS很早就有人提了,不过推进不了,历史债太多
函数中的静态变量为了保证线程安全只有一个,但是开启 -fno-threadsafe-statics 可以生成多个,这个保证有开销吗?基本没有
改进也没swisstable快
不同语言的背景的程序员对于性能慢的接受程度不同,c++的遇到慢的点在他们看来可能不算个事情
作者实现 operator &来简化visitor
看代码
namespace vpp {
namespace detail {
template<typename T>
struct is_specialization_of_variant : std::false_type {};
template<typename... Args>
struct is_specialization_of_variant<std::variant<Args...>> : std::true_type {};
template<typename T>
struct is_variant : is_specialization_of_variant<std::remove_cv_t<std::remove_reference_t<T>>> {};
template<typename T>
constexpr bool is_variant_v = is_variant<T>::value;
template<class... Ts>
struct overload : Ts... {
using Ts::operator()...;
};
// Visitor wrapper
template<class Over>
struct visitor_t {
private:
Over over;
public:
explicit visitor_t(Over o) : over(std::move(o)) {}
// Compose another functor
template<typename U>
auto operator&(U&& u) const {
using UDec = std::decay_t<U>;
return visitor_t<detail::overload<Over, UDec>>{
detail::overload<Over, UDec>{ over, std::forward<U>(u) }
};
}
// Single-variant visit
template<typename V, typename = std::enable_if_t<detail::is_variant_v<V>>>
decltype(auto) operator()(V&& v) const {
return std::visit(over, std::forward<V>(v));
}
// Multi-variant visit
template<typename V1, typename V2, typename... Vs,
typename = std::enable_if_t<
detail::is_variant_v<V1> && detail::is_variant_v<V2>
>>
decltype(auto) operator()(V1&& v1, V2&& v2, Vs&&... vs) const {
return std::visit(over, std::forward<V1>(v1), std::forward<V2>(v2), std::forward<Vs>(vs)...);
}
};
} // namespace detail
constexpr inline struct {
template<typename U>
auto operator&(U&& u) const {
using UDec = std::decay_t<U>;
return detail::visitor_t<detail::overload<UDec>>{ detail::overload<UDec>{ std::forward<U>(u) } };
}
} visitor{};
}
通过visitor,代码可以简化成
void example_unary_visitor() {
std::variant<int, double, std::string> v1 = 42;
std::variant<int, double, std::string> v2 = 3.14;
std::variant<int, double, std::string> v3 = "Hello";
auto to_string_visitor = vpp::visitor
& [](int x) { return std::to_string(x); }
& [](double d) { return std::to_string(d); }
& [](const std::string& s) { return s; };
std::println("[Unary visitor example]");
std::println("v1: {}", to_string_visitor(v1));
std::println("v2: {}", to_string_visitor(v2));
std::println("v3: {}", to_string_visitor(v3));
}
,省几个写lambda括号的时间
godbolt https://cpp2.godbolt.org/z/7x3sf9KoW
simd 环节,这里就不展开了,感兴趣自己看一下
What to do when you have a crash in the runtime control flow guard check
Trying to build a XAML tree in code throws a “No installed components were detected” exception
微信公众号里发了很多,感兴趣的可以看看