从reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。
每周更新
欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue
正常来说有RVO优化 return std::move(s);属于非常多余的动作,会禁止RVO
但是某些场景下,RVO可能有害,也就是逃逸分析(Escape analysis)
作者举了个例子,这个代码段https://godbolt.org/z/jG7x5h 解释了编译器如何分析逃逸,以及这种场景下,无法RVO优化,所以RVO的汇编反而比禁止的要好
也解释了为什么gcc和clang效果不同 -> 在g++中 (x)默认表示禁止RVO,这是一个坑 SO
这篇文章还列了相关的提案,以及自己的建议PATCH,非常建议阅读,对于语言律师识别坑有帮助
自己实现了一个Error类。类似rocksdb的status
结论,除法要比乘慢几倍,编译器有时候能做优化,把除改成乘,比如/2变成*0.5 可以设定-freciprocal-math
总是让编译器做优化
复习一下折叠表达式 比如这段代码 向右折叠,但是会有问题,比如sum()空的
template<typename... Values>
auto sum(Values const&... values)
{
return (values + ...);
}
可以改成向左边折叠
template<typename... Values>
auto sum(Values const&... values)
{
return (0 + ... + values);
}
类似rust的模式匹配语法,很有意思 应该c++23会上
template<auto...> struct ids{};
template<auto N, auto... Ns>
auto dispatch(auto value, ids<N, Ns...>) -> decltype(value) {
return inspect (value) {
N => value;
_ => [] {
if constexpr (sizeof...(Ns) > 0) {
return dispatch(value, ids<Ns...>{});
} else {
return {};
}
}()
};
}
int main() {
std::cout << dispatch(0, ids<1, 2, 3>{}); // prints 0
std::cout << dispatch(4, ids<1, 2, 3>{}); // prints 0
std::cout << dispatch(1, ids<1, 2, 3>{}); // prints 1
std::cout << dispatch(2, ids<1, 2, 3>{}); // prints 2
std::cout << dispatch(3, ids<1, 2, 3>{}); // prints 3
}
这里讲了一个技巧,方便mock singleton
template<class T>
struct singleton {
static auto& get() {
static T s{};
return s;
}
};
class api {
public:
virtual ~api() = default;
virtual auto call() const -> int { return 42; }
};
class app {
public:
auto run() -> int {
return singleton<api>::get().call();
}
};
class app_di {
public:
constexpr explicit(true) app_di(const api& api)
: api_{api}
{ }
auto run() const -> int {
return api_.call();
}
private:
const api& api_;
};
int main() {
{
app a{}; // coupled
assert(42 == a.run());
}
{
app_di a{singleton<api>::get()}; // injected
assert(42 == a.run())
}
{
struct : api {
auto call() const -> int override { return 43; }
} fake_api{};
app_di api{fake_api}; // faked
assert(43 == api.run());
}
}
通过di这个类保持相同的结构,实现不同的api接口塞进去就行了
实际上我见到的大多是一个全局的getter/setter,来实现类似的功能,setter放mock的接口类
没什么新鲜的
作者写了个cp工具,用上了io uring和多线程拷贝,要比cp快,值得看一看代码
设计了几种mutex guard,代码仓库 https://github.com/copperspice/cs_libguarded
几个点
std::mutex设置成mutable这样getter可以const
正常的写法可能就是
int data;
mutable std::mutex mtx;
//////
int getter() const {
std::lock_guard l(mtx);
return data;
}
作者设计了一个封装,结合lock_guard和访问于一身
plain_guarded<int> data;
////
int getter() const {
auto p = data.lock();
return *p;
}
同理,std::shared_mutex对应一个shared_guarded,封装好的类有更好的编译器检查,
如果直接用的mutex,mutex和data的对应关系不明显,可能需要编译器提供GUARD_BY来帮助处理
shared_guarded<int> data;
////
int getter() const {
auto p = data.lock_shared(); //这里的p是const的
return *p;
}
更进一步,把read /write的影响错开,lr_guarded,读写不影响,内部维护两份副本(对于小类型可以,自定义的,可能拷贝代价太大)
读不影响写写不影响读,速度快,这种场景就是RCU了,这里实现了一个rcu_listhttps://github.com/copperspice/cs_libguarded/blob/fcc67e3503a28591532f01476bba5076ffaf272d/src/cs_rcu_list.h
就是这段代码,s1和s2是不一样的
#include <iostream>
#include <string>
int main()
{
std::string str = "hello world";
std::string s1{str,3}; //lo world
std::string s2{"hello world",3}; //hel
std::cout<<s1<<'\n';
std::cout<<s2<<'\n';
}
构造函数重载的问题
第一个匹配了
basic_string( const basic_string& other,
size_type pos,
size_type count = std::basic_string::npos,
const Allocator& alloc = Allocator() );
默认count等于结尾
一个匹配了
basic_string( const CharT* s,
size_type count,
const Allocator& alloc = Allocator() );
count是3
string的构造函数太多太坑爹了