C++ 中文周刊 第104期

周刊项目地址

公众号

RSS https://github.com/wanghenshui/cppweeklynews/releases.atom

欢迎投稿,推荐或自荐文章/软件/资源等

提交 issue

这周瞎忙一周,没来得及看


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2023-03-08 第192期

文章

TL;DR 使用enable_shared_from_this 一定要public继承

一波源码解读,brpc大家都懂懂,争取抄一个自己的rpc

空大讲execution的文章,感兴趣的看看

Daniel Lemire我觉得他的名字应该没人不知道吧。已经提及过很多次,性能专家

std::from_chars已经要比strtod快很多倍了,作者的fast_floatstd::from_chars还快

(前身是fast_double_parser,没切的建议切过去,API和std::from_chars相同)

这篇文章是案例顺带压测一下速度。不用看也知道是吊锤,这个库很多软件都用,比如clickhouse

就是SIMD 接口。没啥说的

#include <immintrin.h>

int main() {
    const std::vector a = {1, 2, 3, 4};
    const std::vector b = {5, 6, 7, 8};

    const auto va = _mm_loadu_si128((__m128i*)a.data());
    const auto vb = _mm_loadu_si128((__m128i*)b.data());
    const auto result = _mm_add_epi32(va, vb);

    std::vector<int> v(a.size());
    _mm_storeu_si128((__m128i*)v.data(), result);

    assert((std::vector{1 + 5, 2 + 6, 3 + 7, 4 + 8} == v));
}

讲trivial relocation的现状以及开源实现

T.r. types Non-t.r. types Throwing-move types Rightward motion (`insert`) Leftward motion (`erase`) Non-pointer iterators
STL Classic (non-relocating) std::copy N/A N/A UB
std::copy_n N/A N/A UB UB
std::copy_backward N/A N/A UB
cstring memcpy UB UB UB SFINAE
memmove UB SFINAE
Qt q_uninitialized_relocate_n ✓? UB UB SFINAE
q_relocate_overlap_n SFINAE
BSL destructiveMove UB UB SFINAE
P2786R0 trivially_relocate SFINAE SFINAE SFINAE
relocate SFINAE SFINAE
move_and_destroy SFINAE UB ?
P1144R6 uninitialized_relocate UB
uninitialized_relocate_n UB
P1144R7 uninitialized_relocate_backward UB

还给人folly提MR https://github.com/facebook/folly/pull/1934

这是个挺复杂的话题。了解一波

还是和trivial relocation相关的实现问题。看不懂

一些模版技巧介绍

CRTP没人不知道吧,想想enable_shared_from_this

overload用法,也就这么个例子

template<typename ... Ts> 
struct Overload : Ts ... { 
    using Ts::operator() ... ; 
};

策略模版,比如

template<class T, class Allocator std::allocator<T>>        
class vector; 

template<class Key,
    class T,
    class Hash = std::hash<Key>,                               
    class KeyEqual = std::equal_to<Key>,                       
    class allocator = std::allocator<std::pair<const Key, T>>  
class unordered_map;

Tag Dispatching 想想迭代器特化

Type Erasure 想想std::function,之前咱们也手挫过一个,往前翻翻回忆一下

先列一段装饰器模式的代码,其实就是策略模式

// taxed.h

#pragma once

#include "money.h"
#include "priced_item.h"
#include <utility>

template< int taxRate, PricedItem Item >
class Taxed : private Item  // Using inheritance
{
 public:
   template< typename... Args >
   explicit Taxed( Args&&... args )
      : Item{ std::forward<Args>(args)... }
   {}

   Money price() const {
      return Item::price() * ( 1.0 + (taxRate/100) );
   }
};

// priced_item.h

#pragma once

#include "money.h"

template< typename T >
concept PricedItem =
   requires ( T item ) {
      { item.price() } -> std::same_as<Money>;
   };

// money.h

#pragma once

#include <cmath>
#include <concepts>
#include <cstdint>
#include <ostream>

struct Money
{
   uint64_t value{};
};

template< typename T >
   requires std::is_arithmetic_v<T>
Money operator*( Money money, T factor )
{
   return Money{ static_cast<uint64_t>( money.value * factor ) };
}

constexpr Money operator+( Money lhs, Money rhs ) noexcept
{
   return Money{ lhs.value + rhs.value };
}

std::ostream& operator<<( std::ostream& os, Money money )
{
   return os << money.value;
}

// discounted.h

#pragma once

#include "money.h"
#include "priced_item.h"
#include <utility>

template< int discount, PricedItem Item >
class Discounted  // Using composition
{
 public:
   template< typename... Args >
   explicit Discounted( Args&&... args )
      : item_{ std::forward<Args>(args)... }
   {}

   Money price() const {
      return item_.price() * ( 1.0 - (discount/100) );
   }

 private:
   Item item_;
};

// cpp_book.h

#pragma once

#include "money.h"

#include <string>
#include <utility>

class CppBook
{
 public:
   CppBook( std::string name, Money price )
      : name_{ std::move(name) }
      , price_{ price }
   {}

   std::string const& name() const { return name_; }
   Money price() const { return price_; }

 private:
   std::string name_;
   Money price_;
};

// main.cpp

#include "conference_ticket.h"
#include "cpp_book.h"
#include "discounted.h"
#include "taxed.h"

#include <cstdlib>



int main()
{
   // 20% discount, 15% tax: (499*0.8)*1.15 = 459.08
   Taxed<15,Discounted<20,ConferenceTicket>> item{ "Core C++", Money{499} };
   Taxed<16,Discounted<21,ConferenceTicket>> item2{ "Core C++", Money{499} };
   Taxed<17,Discounted<22,CppBook>> item3{ "Core C++", Money{499} };

   Money const totalPrice = item.price();  // Results in 459.08
   Money const totalPrice2 = item2.price();
   Money const totalPrice3 = item3.price();
      // ...

   return EXIT_SUCCESS;
}   

比继承快多了

MSVC的能力介绍

chrome编译符号太大了。只能升级工具来解决这个问题。作者的吐槽文

组合函数,高阶函数,一个常规的写法

template <class F, class... Fs> 
constexpr auto compose(F &&arg, Fs &&...args) {
  return [
    fun = std::forward<F>(arg),
    ... functions = std::forward<Fs>(args)
  ] <class... Xs> (Xs &&...xs) mutable
    requires std::invocable<F, Xs...> {
    if constexpr (sizeof...(Fs)) {
      return compose(std::forward<Fs>(functions)...)(
        std::invoke(std::forward<F>(fun), 
                    std::forward<Xs>(xs)...));
    } else {
      return std::invoke(
        std::forward<F>(fun), 
        std::forward<Xs>(xs)...);
    }
  };
}

辣眼睛。有没有人类能看懂的?std::views::transform

using std::views::transform;
auto fgh = transform(h) | transform(g) | transform(f);
 
// Calculate f( g( h(x) ) )
auto fgh_x = ranges::single_view{42} | fgh; 
 
//fgh_x[0]

豁然开朗了家人们

template <class... Fs>
auto composer(Fs&&... functions) {
  using std::views::transform;
  return (transform(functions) | ...);
}

leetcode 56题,用stl算法怎么做?std::partial_sum

sort(intervals);
auto merged = views::partial_sum(intervals, [](auto curr, auto i){
    return curr.second >= i.first ? std::pair{curr.first, max(curr.second, i.second)} : i;
}) | views::adjacent_remove_if([](auto i1, auto i2){
    return i1.first == i2.first;
});
 
for (const auto& [i,j] : merged) {
    std::cout << i << " " << j << "\n";
}

很精彩的讲SWAR的文章,我还没有看完

hyperfine可以分析单测执行快慢。建议执行单测可以前面加上这个跑,每次统计测试运行时间长短,来分析代码哪里引入问题,这也是一个有趣的思路

总之测试压测之类的数据,都是有用的

代码分析慢的地方是结构体比较 opetator==慢了。这个没啥好讲的,主要在于前面发现问题的思路,提前发现问题,解决问题

老生常谈了。reserve提高局部性,改动只要一点点

 std::vector<std::string> tokenize(const std::string& s) {
   std::vector<std::string> result;
+  // Expect four fields or less in our input.
+  result.reserve(4);
   std::string::size_type f = 0;
   std::string::size_type p = s.find(':');

开源项目需要人手

新项目介绍/版本更新


本文永久链接

如果有疑问评论最好在上面链接到评论区里评论,这样方便搜索,微信公众号有点封闭/知乎吞评论

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账