公众号
RSS https://github.com/wanghenshui/cppweeklynews/releases.atom
欢迎投稿,推荐或自荐文章/软件/资源等
感谢不语
赞助
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-08-09 第214期
boost::async review https://github.com/klemens-morgenstern/async
https://www.reddit.com/r/cpp/comments/15l8xju/review_of_proposed_boostasync_begins/
比较集成在ASIO上
感觉cppwinrt还没开发多久,就维护状态了,又引入了新库 https://github.com/microsoft/wil/
说实话,微软总搞这种事,开发一个新技术,让大伙学,还没怎么学明白,换坑,再重新学,和google有一拼
之前更新了很多winrt的文章,得亏一个没看,他妈的,纯纯浪费生命,微软我___
auto foo() { return 42; }
int main() {
auto unused = foo(); // warning in C++23
auto _ = foo(); // no warning in C++26
}
话说,我以前是这么玩的, std::ignore
decltype(std::ignore) _;
_ = blahblah();
介绍链表实现,MSVC和gcc/clang的实现还不太一样, 单向链表都差不多
template<typename T>
struct forward_list {
forward_list_node<T>* head;
};
template<typename T>
struct forward_list_node {
forward_list_node<T>* next;
T value;
};
双向链表,有个边界判定问题
template<typename T>
struct list {
list_node_base<T> head; // or "list_node_base<T>* head;"
size_t size;
};
template<typename T>
struct list_node_base {
list_node<T>* next;
list_node<T>* prev;
};
template<typename T>
struct list_node : list_node_base<T> {
T value;
};
可以有个dummy node来管理开头结尾,也可不用,多一个内存分配,这也是msvc需要注意的地方,list构造可能抛异常
基本结构就这样
struct tree {
node_base header; // or "node_base* header;"
size_t size;
};
struct node_base {
node_base* parent;
node_base* left;
node_base* right;
};
struct node : node_base {
bool color; // red or black
payload data;
};
介绍几种高效的用法,比如vector
reserve
+ emplace_back
,这种大家都知道,代码就不贴了
lambda 捕获优化
auto result = std::find_if(vs.begin(), vs.end(),
[&prefix](const std::string& s) {
return s == prefix + "bar"s;
}
);
这种使用捕获用法,每次都要生成对象,效率低下,需要改成
result = std::find_if(vs.begin(), vs.end(),
[savedString = prefix + "bar"s](const std::string& s) {
return s == savedString;
}
);
这样,避免每次lambda都构造
unique_ptr
优化 make_unique_for_overwrite
,有时候你想用unique_ptr
管理 buffer,但没有必要清零,可以用这个接口,类似string
的 resize_for_overwrite
auto ptr = std::make_unique_for_overwrite<int[]>(1000);
另外评论区@star-hangxing 指出,可以用 unique_ptr<int[]>(new int[count])
感谢
pair/tuple优化 piecewise_construct
forward_as_tuple
// 1
std::cout << "regular: \n";
std::pair<MyType, MyType> p { MyType{"one", 1}, MyType{"two", 2}};
// 2
std::cout << "piecewise + forward: \n";
std::pair<MyType, MyType>p2(std::piecewise_construct,
std::forward_as_tuple("one", 1),
std::forward_as_tuple("two", 2));
起到一个就地构造的效果,类似emplace_back
对于map来说,这种也能用
#include <string>
#include <map>
struct Key {
Key(int a, int b) : sum(a + b) {}
int sum;
bool operator<(const Key& other) const {
return sum < other.sum;
}
};
struct Value {
Value(const std::string& s, double d) : name(s), data(d) {}
std::string name;
double data;
};
int main() {
std::map<Key, Value> myMap;
// doesn't compile: ambiguous
// myMap.emplace(3, 4, "example", 42.0);
// works:
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(3, 4),
std::forward_as_tuple("example", 42.0)
);
}
consteval bool all_of(const auto& f, const auto&... xs) {
return (f(xs) && ...);
}
consteval bool contains(const auto& n, const auto&... hs) {
return ((hs == n) || ...);
}
consteval int count(const auto& n, const auto&... hs) {
return (0 + ... + (hs == n));
}
static_assert(count(1, 3,1,4,1,6) == 2);
static_assert(count(2, 3,1,4,1,6) == 0);
这都很常规
类似的,可以写个判断重复
consteval bool has_any_duplicates() { return false; }
consteval bool has_any_duplicates(const auto& n, const auto&... hs) {
return ((n == hs) || ...) || has_any_duplicates(hs...);
}
static_assert(has_any_duplicate(3,1,4,1,6));
static_assert(!has_any_duplicate(2,7,1,8,3));
static_assert(has_any_duplicate(9,9,9));
static_assert(!has_any_duplicate(9));
static_assert(!has_any_duplicate());
有点递归了,感觉和count有点像?用count改写
constexpr auto has_duplicate_of(const auto& value) {
return [&](const auto&... hs) {
return count(value, hs...) >= 2;
};
}
consteval bool has_any_duplicates(const auto&... hs) {
return (has_duplicate_of(hs)(hs...) || ...);
}
再简化一下,不用 has_duplicate_of
consteval bool has_any_duplicates(const auto&... hs) {
return ((count(hs, hs...) >= 2) || ...);
}
不用count再简化一下
consteval bool has_any_duplicates(const auto&... hs) {
return ([&](const auto& n) { return (0 + ... + (hs == n)) >= 2; }(hs) || ...);
}
还有优化空间! 参数n可以优化
consteval bool has_any_duplicates(const auto&... hs) {
return ([&,&n=hs]{ return (0 + ... + (hs == n)) >= 2; }() || ...);
}
手把手教你写SIMD代码。看晕了 代码在这里https://github.com/oliora/habr-switches-perf-test
hashtable的内存结构,基本就这样
struct hashtable{
using hint = std::list<payload>::iterator;
std::list<payload> list;
std::vector<hint> buckets;
};
开链在现在的硬件看来已经证明是缓存不友好的了,flatmap可以解决这一点
可以这样理解
template<typename T>
struct simple_deque {
T* elements;
T* first;
T* last;
size_t capacity;
};
/*
first last
elements | ? | ? | ? | ? | 1 | 2 | 3 | ? |
size = last - first =3
cap = 8
*/
大概就是这样的结构 从中间往外展开,如果空间用光,就分配alloc一条新的数组,然后管理数组间的串联关系
列举各种实现的差异
| | gcc | clang | msvc |
| —————————– | ———————————————————————————- | —————————————————————————————————– | ——————————————————————- |
| Block size | as many as fit in 512 bytes but at least 1 element | as many as fit in 4096 bytes but at least 16 elements | power of 2 that fits in 16 bytes but at least 1 element |
| Initial map size | 8 | 2 | 8 |
| Map growth | 2× | 2× | 2× |
| Map shrinkage | On request | On request | On request |
| Initial first/last | Center | Start | Start |
| Members | block** map; size_t map_size;
iterator first;
iterator last; | block** map;
block** first_block;
block** last_block;
block** end_block;
size_t first;
size_t size; | block** map; size_t map_size;
size_t first;
size_t size; |
| Map layout | counted array | simple_deque | counted array |
| Valid range | Pair of iterators | Start and count | Start and count |
| Iterator | T* current;
T* current_block_begin;
T* current_block_end;
block** current_block; | T* current; block** current_block; | deque* parent;
size_t index; |
| begin()
/
end()
| Copyfirst
andlast
. | Breakfirst
andfirst + size
into block index and offset. | Breakfirst
andfirst + size
into block index and offset. |
| Spare blocks | Aggressively pruned | Keep one on each end | Keep all |
看晕了
老文章,看代码
consteval auto as_constant(auto value) {
return value;
}
constexpr int Calc(int x) { return 4 * x; }
//consteval int Calc(int x) { return 4 * x; }
int main() {
auto res = Calc(2);
// auto res = as_constant(Calc(2));
++res;
res = Calc(res); //编译不过
return res;
}
如果改成as_constant就能编译过。as_constant看起来傻逼,但是强制编译期计算了
之前代码抄错 #68 @fanenr 指正,这里表示感谢
造轮子,没觉得有啥意思 代码这里 https://github.com/PipeRift/pipe/blob/feature/custom-arrays/Include/Pipe/PipeArrays.h
看代码, 多维数组访问下标
#include <vector>
#include <https://raw.githubusercontent.com/kokkos/mdspan/single-header/mdspan.hpp>
#include <iostream>
int main()
{
std::vector v = {1,2,3,4,5,6,7,8,9,10,11,12};
// View data as contiguous memory representing 2 rows of 6 ints each
auto multispan = std::experimental::mdspan(v.data(), 2, 6);
std::cout << multispan[0, 1] << '\n'; // 2
}
想让对象通过特别的factory来构造。自己不能构造
看代码。没啥说的,就是private + tag类限制
tag类甚至可以不用
class Secret {
class ConstructorKey {
friend class SecretFactory;
private:
ConstructorKey() {};
ConstructorKey(ConstructorKey const&)
= default;
};
public:
//Whoever can provide a key has access:
explicit Secret(std::string str,
ConstructorKey) : data(std::move(str)) {}
private:
//these stay private, since Secret itself has
// no friends any more
void addData(std::string const& moreData);
std::string data;
};
class SecretFactory {
public:
Secret getSecret(std::string str) {
return Secret{std::move(str), {}};
}
// void modify(Secret& secret,
// std::string const& additionalData) {
// secret.addData(additionalData); //ERROR:
// // void Secret::addData(const string&)
// // is private
// }
};
int main() {
Secret s{"foo?", {}}; //ERROR:
// Secret::ConstructorKey::ConstructorKey()
// is private
SecretFactory sf;
Secret s = sf.getSecret("moo!"); //OK
}
无栈协程各种缺点
task<void> async_insert(T && val);
task<void> async_find(const T &);
task<void> async_write(span<byte>);
这种不注意生命周期一不留神就用错
task<void> send_all(string s) {
for (auto & source : m_sources)
{
co_await source.send(s);
}
}
看上去没问题,如果m_sources有改动,就完了
所以复制一下是不是就没问题了?
task<void> send_all(string s)
{
std::vector<task<void>> sends;
sends.reserve(m_sources.size());
for (auto & source : m_sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
}
等一下,m_sources如果析构了呢?
保活一下
task<void> send_all(string s)
{
std::vector<task<void>> sends;
std::vector<shared_ptr<Source>> sources = m_sources;
sends.reserve(sources.size());
for (auto & source : sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
}
或者有个爹
struct Source
{
std::vector<std::coroutine_handle> dependent_coroutines;
~Source()
{
for (auto & coroutine : dependent_coroutines)
coro.destroy();
}
task<void> send(string s)
{
auto corohandle = co_await get_current_coroutine{};
dependent_coroutines.push_back( corohandle );
...
dependent_coroutines.erase( dependent_coroutines.find( corohandle ) );
}
}
或者加个锁?
task<void> send_all(string s)
{
co_await m_sourcesLock.read_lock();
std::vector<task<void>> sends;
sends.reserve(m_sources.size());
for (auto & source : m_sources)
{
sends.push_back(source->send(s));
}
co_await wait_all( sends.begin(), sends.end() );
co_await m_sourcesLock.read_unlock();
}
已经想吐了
co_await fetch_data("key");
这代码的问题相信你能看出来 key的生命周期是临时的。如果立即执行这个协程,不会崩,你显然没发现这个bug
然后这种代码越来越多,突然某一天就崩了,你还在纳闷咋回事
哦你发现了,想了想,保存一下,延长一下生命周期,应该行了吧
eager_task<string> fetch_data(string_view key)
{
auto it = cache.find(key)
if (it != cache.end())
{
return it->second;
}
auto data = co_await fetch_remote(key);
cache.emplace(key, data);
}
但是指不定某个兄弟就这么写了
eager_task<void> fetch_mydata(string_view key)
{
return fetch_data(std::format("mysystem/{}", key));
}
同样的崩溃问题又出现了
测试问题,怎么避免上面这种用法的出现?await_transform?
性能问题,内存分配的浪费?即使有HALO优化,还是会有很大的栈空间浪费?
还是性能问题,生成的函数太多了,debug build很多浪费,这个无解,只能等编译器优化
讲的玩意类似taskflow,但是是用hash来串联类型依赖。没有源代码
讲函数怎么写assert写约束写断言,还算有点意思,周末整理一下这个
有没有数据库相关的工作推荐我一下,我要失业了快
如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论