写点什么

C++ 动态新闻推送 第 35 期

作者:很水
  • 2021 年 11 月 12 日
  • 本文字数:4910 字

    阅读完需:约 16 分钟



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


每周更新


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


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



资讯

编译器信息最新动态推荐关注 hellogcc 公众号

OSDT Weekly 2021-10-27 第121期


标准会十月邮件列表

文章


#include <initializer_list>#include <iostream>
int main() { for (using T = int; T e : {1, 2}) { std::cout << e; // prints 1,2 }
for (struct T { int x; int y; }; T e : {T{1,2}, T{3,4}}) { std::cout << "{" << e.x << ',' << e.y << '}'; // prints {1,2}{3,4} }}
复制代码


if 里面啥花活都能整了属于是


godbolt体验



struct Widget {};
namespace std { // Danger! template<> struct hash<Widget> { size_t operator()(const Widget&) const; };}
//这样写,不要上面那种写法struct Widget {};
template<>struct std::hash<Widget> { size_t operator()(const Widget&) const;};
复制代码



怎么保证一个函数只被调用一次呢,这里有个点子,Destructive separation: move away and call Matt Godbolt and his talk at C++ On Sea 2020.


#include <iostream>
class CostlyResult{};
class MyClass {public: // ... [[nodiscard]] CostlyResult getCostly() && { return {}; }private:};
int main() { MyClass mc; auto r = mc.getCostly();}
复制代码


这样调用会报错,因为你不是 move 的不能调用


于是就可以这样调用


auto r = std::move(mc).getCostly();
复制代码


从而保证了一次调用,和生命周期同步了


但是,你要是这样调用


auto r = std::move(mc).getCostly();auto r2 = std::move(mc).getCostly();
复制代码


也拦不住。不过后面有篇文章继续讨论了这个话题



void DoSomething(const Configuration& p){   // ...} class ConfigurationBuilder{public:   ConfigurationBuilder& SetName(string name)   {       m_data.name = move(name);       return *this;   }    ConfigurationBuilder& SetFolderPath(path folderPath)   {       m_data.folderPath = move(folderPath);       return *this;   }   // ...    Configuration Build()   {      return m_data;   }private:   Configuration m_data;}; //... auto conf = ConfigurationBuilder{}.                  SetName("marco").                  Build();DoSomething(conf);
复制代码


这段代码的问题在于,不能保证别人执行了 Build 这行代码,也不能保证所有代码都只执行一次,怎么做?加上类型判定 + move


完整代码在这里


首先,有个全局的标记数组,这个数组可以编译期算值


namespace utils{    template<typename... Pack>    struct pack    {         template<typename T>         static constexpr ssize_t index_of = []{                 constexpr array<bool, sizeof...(Pack)> bits {{ is_same<T, Pack>::value... }};                const auto it = find(begin(bits), end(bits), true);                return it != end(bits) ? distance(begin(bits), it) : -1;         }();                   template<typename T>         static constexpr bool has = []{                 return index_of<T> != -1;         }();    };}
复制代码


直接 has 判断这个类型对应的 flag 是不是标记了


然后,定义各种 tag 类型


namespace tags{    struct set_name_called{};    struct set_folder_called{};} struct Configuration{    std::string name;    std::filesystem::path folderPath;}; template<typename... Tags>class ConfigurationBuilder{public:    ConfigurationBuilder<tags::set_name_called, Tags...> SetName(string name) &&    {       static_assert(utils::pack<Tags...>::template index_of<tags::set_name_called> == -1, "'SetName' has already been called!");       m_data.name = move(name);       return {move(m_data)};    }         ConfigurationBuilder<tags::set_folder_called, Tags...> SetFolderPath(path folderPath) &&    {       static_assert(utils::pack<Tags...>::template index_of<tags::set_folder_called> == -1, "'SetFolderPath' has already been called!");       m_data.folderPath = move(folderPath);       return {move(m_data)};    }     Configuration Build() &&    {        static_assert(utils::pack<Tags...>::template index_of<tags::set_name_called> != -1, "'SetName' is mandatory");        static_assert(utils::pack<Tags...>::template index_of<tags::set_folder_called> != -1, "'SetFolderPath' is mandatory");        return move(m_data);    }private:    ConfigurationBuilder() = default;     ConfigurationBuilder(Configuration c)       : m_data(move(c))    {    }         template<typename... K>    friend class ConfigurationBuilder;     friend ConfigurationBuilder<> BuildConfiguration();     Configuration m_data;}; ConfigurationBuilder<> BuildConfiguration(){ return{}; }
复制代码


第一次调用,没问题,标记,第二次调用,不满足条件,static_assert 报错


不过,这个措施,有点点复杂


然后 tag,有各种分类,在用继承之类的扩展



设置 github 项目支持微软代码分析工具



作者看汇编发现原来 printf 是 puts 实现/替换的



一个向量化优化策略


if (x > y) {  do_something();} else {  do_something_else();}
复制代码


优化成


if (x > y) {  do_something();}if (x <= y) {  do_something_else();}
复制代码


当 x y 不是 NaN 就可以这样优化


-ffinite-math-only告诉编译器,没有 NaN,大胆去优化,但是如果 x y 恰巧是 NaN,那就完了


一个汇编例子 godbolt


float a[1024];float b[1024];
void foo(void) { for (int i = 0; i < 1024; ++i) { if (b[i] > 42.0f) { a[i] = b[i] + 1.0f; } else { b[i] = a[i] + 1.0f; } }}
复制代码


如果开了优化,b[i] 恰巧是 NaN,那就完了,哪个 if 都不走


怎么处理这种问题?没有优雅的办法,这样也许可以


feenableexcept(FE_OVERFLOW | FE_INVALID | FE_DIVBYZERO);
复制代码


但不优雅。如果开启这个优化,务必了解你的代码会不会有 NaN。能精细的控制优化的前提是扣掉某些场景。如果你的场景包含 NaN,就别用这个优化



实现<=>也得用 friend 惯用法,和其他的比较操作符类似,不然可能会有找不到调用的问题


struct Good {    friend auto operator<=>(const Good&, const Good&) = default;};
struct Bad { auto operator<=>(const Bad&) const = default;};
static_assert(std::totally_ordered<Good>);static_assert(std::totally_ordered<Bad>);
static_assert(std::totally_ordered<std::reference_wrapper<Good>>);static_assert(not std::totally_ordered<std::reference_wrapper<Bad>>); // !!
复制代码



简单来说是的 deque 的 empty 就要比 size 快


但是有些自己实现的 empty 可能不一定比 size == 0 快。实现可能有问题



讨论了一些场景的返回值是否会被优化掉,copy elision的生效场景



讨论 NTTP(Non-Type Template Parameters) 的用处,比如


template <size_t Length>struct fixed_string {    char _chars[Length+1] = {}; // +1 for null terminator};template <size_t N>fixed_string(const char (&arr)[N])    -> fixed_string<N-1>;  // Drop the null terminator
复制代码



实现 ts 里类型检查,类似


type foo = { first: string, last: string };
const o = { first: "Foo", last: "Oof", age: 30 };const p = { first: "Bar", last: "Rab", age: 45 };const q = { first: "Baz", last: "Zab", gender: "m" };
const main = <T extends foo>(o: T) => (p: T) => o.first + o.last
main(o) (p); // type checksmain(o) (q); // type error
复制代码


基本想法


import Mitama.Data.Extensible.Record;#include <iostream>#include <format>
using namespace mitama::literals;using namespace std::literals;
void print(mitama::has<"name"_, "age"_> auto person) { std::cout << std::format("name = {}, age = {}\n", person["name"_], person["age"_]);}
int main() { using mitama::as; // declare record type using Person = mitama::record < mitama::named<"name"_, std::string> , mitama::named<"age"_, int> >;
// make record Person john = Person{ "name"_v = "John"s, "age"_v = 42, };
// access to rows john["name"_]; // "John" john["age"_]; // 42
print(john); // OK
auto tom = mitama::empty += as<"name"_>("Tom"s) ;
print(tom); // ERROR: constraints not satisfied}
复制代码


考虑如何实现?代码在这里


基本上是 UDL 实现 name_ age_ ,然后用 fix_string 装起来,然后再判断不同的 fix_string 类型



google 实现 c++上的 borrow checker 遇到的困难



写移植 Renderer 遇到的问题,文章很长。这方面我不太懂,这里标记个 TODO,后面补充



还是讨论可变返回类型


#include <iostream>#include <typeinfo>#include <type_traits>
template <typename T, typename T2>auto sum(T t, T2 t2) -> decltype(t + t2) { return t + t2;}

int main() {
std::cout << '\n';
std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double std::cout << typeid(sum(5.5, true)).name() << '\n'; // double std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double std::cout << typeid(sum(true, false)).name() << '\n'; // int
std::cout << '\n';
}
复制代码


c++20


#include <iostream>#include <typeinfo>#include <type_traits>
template<typename T>concept Arithmetic = std::is_arithmetic<T>::value;
Arithmetic auto sum(Arithmetic auto t, Arithmetic auto t2) { return t + t2;}

int main() {
std::cout << '\n';
std::cout << typeid(sum(5.5, 5.5)).name() << '\n'; // double std::cout << typeid(sum(5.5, true)).name() << '\n'; // double std::cout << typeid(sum(true, 5.5)).name() << '\n'; // double std::cout << typeid(sum(true, false)).name() << '\n'; // int
std::cout << '\n';
}
复制代码

视频


在线评价别人的代码中的 API 设计是不是合理



教你用协程写个 parser,代码在这里

项目




看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持!


本文永久链接

发布于: 1 小时前阅读数: 4
用户头像

很水

关注

还未添加个人签名 2020.10.21 加入

还未添加个人简介

评论

发布
暂无评论
C++ 动态新闻推送 第35期