C++ 中文周刊 第50期

reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态

周刊项目地址在线地址知乎专栏 腾讯云+社区

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


资讯

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

c++ summit在上海要开,三月份,两天套票接近六千,真心贵,这价格,比cppcon还贵

Visual Studio 2022 17.1 is now available!

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2022-02-16 第137期

文章

constexpr auto foo = [] [[deprecated]] { };

int main() {
    foo(); // operator() is deprecated
}

Lambda 可以标注

主要是利用clang的 -ftime-trace 参数

我记得gcc也有一个类似的找不到了

承接上文啊,能实现遍历打印,肯定也能实现遍历调用lambda,如何实现呢?

核心代码,之前的index_sequence搬过来,另外还需要展开变参模版

for_each_tuple和之前的printtuple类似,for_each_tuple2避免难理解,主要是依赖lambda的模版能力,也是要展开变参模版

template <typename TupleT, typename Fn, std::size_t... Is>
void for_each_tuple_impl(TupleT&& tp, Fn&& fn, std::index_sequence<Is...>) {
    (fn(std::get<Is>(std::forward<TupleT>(tp))), ...);
}

template <typename TupleT, typename Fn, std::size_t TupSize = std::tuple_size_v<std::remove_cvref_t<TupleT>>>
void for_each_tuple(TupleT&& tp, Fn&& fn) {
    for_each_tuple_impl(std::forward<TupleT>(tp), std::forward<Fn>(fn), std::make_index_sequence<TupSize>{});
}

template <typename TupleT, typename Fn>
void for_each_tuple2(TupleT&& tp, Fn&& fn) {
	std::apply
	(
		[&fn]<typename ...T>(T&& ...args)
		{
			(fn(std::forward<T>(args)), ...);
		}, std::forward<TupleT>(tp)
	);
}

虽然一般来说不需要拷贝的传参数用const T&就万事大吉,但是有些场景是不行的,比如T的设计不合理

我们要考虑T设计的问题,另外小对象,不要用const T&,比如string_view span int这种 直接传value

突然得知executor进不了c++23了,哎可惜。这些概念了解一下还是可以的。抽象程度很高

讨论了一下把返回值放到后面的可行性,主要原因是作者开发的库经常会遇到这个返回值类型不确定的场景,比如

template<typename A, typename B>
decltype(std::declval<A>() * std::declval<B>()) multiply(A a, B b) { return a*b; }

标准库的算法,了解一下

考虑一种场景,成员函数修饰调用限定

void Foo::bar() & { /* ... */ }
void Foo::bar() && { /* ... */ }
void Foo::bar() const & { /* ... */ }
void Foo::bar() const && { /* ... */ }

后面这种场景是为了限定Foo在某些类型的场景下才能调用

有了deducing this就能简化。举个例子

template <typename T>
class OptionalNotDeducingThis {
  // ...
  constexpr T* operator->() {
    return addressof(this->m_value);
  }

  constexpr T const*
  operator->() const {
    return addressof(this->m_value);
  }
  // ...
};

template <typename T>
class OptionalDeducingThis {
  // ...
  template <typename Self>
  constexpr auto operator->(this Self&& self) {
    return addressof(self.m_value);
  }
  // ...
};

this Self来决定auto,所以你要const就 T* const,你不const的就T*

一个证书序列化成字符串的算法(itoa)比fmt库内部的算法还要快,不过fmt作者没有考虑使用这个算法。

这里简单介绍一下

最简单的写法

char* itoa_naive(std::uint32_t n, char* buffer) {
  char temp[10];
  char* ptr = temp + sizeof(temp) - 1;
  while (n >= 10) {
    *ptr = char('0' + (n % 10));
    n /= 10;
    --ptr;
  }
  *ptr = char('0' + n);
  auto length = temp + sizeof(temp) - ptr;
  std::memcpy(buffer, ptr, length);
  return buffer + length;
}

把整数序列化到temp数组,再拷贝出去,buf是10是因为int32就那么大

显然循环除10很慢,我们可以考虑减少循环次数,然后考虑除100

然后直接把余数给算好

首先想到的优化就是查表写数,而不是计算

static constexpr char radix_100_table[] = {
    '0', '0', '0', '1', '0', '2', '0', '3', '0', '4',
    '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
    '1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
    '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
    '2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
    '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
    '3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
    '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
    '4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
    '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
    '5', '0', '5', '1', '5', '2', '5', '3', '5', '4',
    '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
    '6', '0', '6', '1', '6', '2', '6', '3', '6', '4',
    '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
    '7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
    '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
    '8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
    '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
    '9', '0', '9', '1', '9', '2', '9', '3', '9', '4',
    '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
};

char* itoa_two_digits_per_div(std::uint32_t n, char* buffer) {
  char temp[8];
  char* ptr = temp + sizeof(temp);
  while (n >= 100) {
    ptr -= 2;
    std::memcpy(ptr, radix_100_table + (n % 100) * 2, 2);
    n /= 100;
  }
  if (n >= 10) {
    std::memcpy(buffer, radix_100_table + n * 2, 2);
    buffer += 2;
  }
  else {
    buffer[0] = char('0' + n);
    buffer += 1;
  }
  auto remaining_length = temp + sizeof(temp) - ptr;
  std::memcpy(buffer, ptr, remaining_length);
  return buffer + remaining_length;
}

说实话这块我就看不懂了,后面更难了。这里标记个TODO,有时间研究一下

作者的一些优化经验,我看lamire老哥也关注了。写的很有噱头

文章很长一时半会看不完,这里先标记TODO了

struct Person {
    std::string first;
    std::string last;
};

std::vector<Person> people = { /* ... */ };
std::vector<std::string> r_names;
std::ranges::copy_if(
        people,
        std::back_inserter(r_names),
        [](std::string const& s) { return s[0] == 'R'; },
        &Person::last);

std::ranges::copy_if(
        people | std::views::transform(&Person::last),
        std::back_inserter(r_names),
        [](std::string const& s) { return s[0] == 'R'; });

看懂这两段代码的区别了吗,第一段代码不工作,因为

视频

没啥说的

有时间可以看一下。simd感觉早晚得了解,躲不开

这里标记一个TODO,后面看了总结一下

开源项目需要人手

新项目介绍/版本更新


本文永久链接

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