C++ 中文周刊 第97期

周刊项目地址

公众号

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

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

提交 issue

祝大家新年快乐。

资讯

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

一月邮件列表

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/#mailing2023-01

编译器信息最新动态推荐关注hellogcc公众号 OSDT Weekly 2023-01-18 第185期

文章

笑死 #embed 早晚玩出花来

https://godbolt.org/z/Tj6c7jz1o

#include <string_view>
#include <array>

template <std::size_t N>
struct fixed_string final {
    constexpr explicit(true) fixed_string(const auto... cs) : data{cs...} {}

    constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
        std::copy_n(str, N + 1, std::data(data));
    }

    [[nodiscard]] constexpr auto operator<=>(const fixed_string&) const =
        default;

    [[nodiscard]] constexpr explicit(false) operator std::string_view() const {
        return {std::data(data), N};
    }

    [[nodiscard]] constexpr auto size() const -> std::size_t { return N; }

    std::array<char, N + 1> data{};
};

template <std::size_t N>
fixed_string(const char (&str)[N]) -> fixed_string<N - 1>;
fixed_string(const auto... Cs) -> fixed_string<sizeof...(Cs)>;

template<fixed_string Name>
constexpr auto meta_contains = [] {
  static constexpr char self[] = { 
    #embed __FILE__ 
  };
  const auto code = std::string_view(std::data(self), std::size(self));
  const auto find = code.find(Name);
  return find != std::string_view::npos and code[find-1] != '\"';
}();

struct foo {};
struct bar {};

auto fn() -> void;

static_assert(not meta_contains<"struct x">);
static_assert(not meta_contains<"STD::string_view">);
static_assert(meta_contains<"std::string_view">);
static_assert(meta_contains<"struct foo">);
static_assert(meta_contains<"struct bar">);
static_assert(meta_contains<"auto fn()">);

再学点range

adjacent

std::vector v = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (auto const& t : v | std::views::adjacent<3>) {
   std::cout << '[' << std::get<0>(t) << ',' 
                    << std::get<1>(t) << ',' 
                    << std::get<2>(t) 
             << ']' << '\n';
} 
/*
[1,2,3]
[2,3,4]
[3,4,5]
[4,5,6]
[5,6,7]
[6,7,8]
*/

//也可以这样

for (auto const& [a,b,c] : v | std::views::adjacent<3>) {
   std::cout << '[' << a << ',' << b << ',' << c << ']' << '\n';
}

slide和adjacent差不多,代码贴一下

auto print(R&& r) {
   std::cout << '[';
   bool first = true;
   for (auto const e : r) {
      if(first) first = false;
      else std::cout << ',';
       
      std::cout << e;
   }
   std::cout << ']' << '\n';
}
std::vector v = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (auto const& c : v | std::views::slide(3)) {
   print(c);
}

/*
[1,2,3]
[2,3,4]
[3,4,5]
[4,5,6]
[5,6,7]
[6,7,8]
*/

chunk,分段

for (auto const& c : v | std::views::chunk(3)) {
   print(c);
}
/*
[1,2,3]
[4,5,6]
[7,8]
*/

chunk_by类似chunk,能提供一个谓词判断

bool differ_by_one(int const a, int const b)
{
    return std::abs(a - b) <= 1;
}
std::vector v = {1,1,2,3,2,2,1,3,4,8,7,6,7};
for (auto const& c : v | std::views::chunk_by(differ_by_one))
{
   print(c);
}
/* 按照相邻差1来分段 
[1,1,2,3,2,2,1]
[3,4]
[8,7,6,7]
*/

再来个例子

bool same_kind(char const a, char const b)
{
    bool b1 = std::isdigit(a);
    bool b2 = std::isdigit(b);
    return (b1 && b2) || (!b1 && !b2);
}
std::string s {"1234abc56e789fghi"};
for (auto const& c : s | std::views::chunk_by(same_kind))
{
   print(c);
}
/*
按照字母类型来分段

[1,2,3,4]
[a,b,c]
[5,6]
[e]
[7,8,9]
[f,g,h,i]

*/
#include <iostream>
class MyDistance{
 public:
    explicit MyDistance(double i):m(i){}
    friend MyDistance operator +(const MyDistance& a, const MyDistance& b){         // (1)
        return MyDistance(a.m + b.m);
    } 
    friend MyDistance operator -(const MyDistance& a, const MyDistance& b){         // (2)
        return MyDistance(a.m - b.m);
    }
    friend std::ostream& operator<< (std::ostream &out, const MyDistance& myDist){  // (3)
        out << myDist.m << " m";
        return out;
    }  
 private:
    double m;
};

int main() {
    std::cout << "MyDistance(5.5) + MyDistance(5.5): " << MyDistance(5.5) + MyDistance(5.5) << '\n';  // (4)
    std::cout << "MyDistance(5.5) - MyDistance(5.5): " << MyDistance(5.5) - MyDistance(5.5) << '\n';  // (5)
}

没啥说的,没有friend就找不到这几个operator函数,而且这么写也不用非得是成员函数

好像之前说过这个?反复强调,undefined behavior未定义行为不等于实现定义,有可能是历史遗留问题,也有可能就毁灭地球

逆天用法

std::vector<unsigned char> getFavicon() {
  return {
    #embed "favicon.ico"
  };
}

这种场景embed可能退化成initializer_list,复制到栈上,然后再复制到vector,堆上,白白浪费

#embed就老老实实当属性二进制用,这种写法也不是不行,字符串不大也可以

作者写了个提案,方便解决这种情况。具体没看,大概就是识别优化掉这玩意

看段代码

#include <array>

struct Node {
    int a = 1, b = 1;
};

std::array <Node, 10'000> a;

int main() {}

问题在于array占用80k额外空间,加上Node初始化占用多余的空间,怎么样能省?初始化可以用0,省掉,本身全局变量并没有很好的办法优化掉

视频

介绍静态分析工具 https://github.com/secure-software-engineering/phasar

看不懂

代码在这里 https://github.com/sslotin/amh-code

他也是咱们之前提到过很多次的这个博客 https://en.algorithmica.org/hpc/ 的作者。

这个讲的就是这个博客的内容,如何优化binary search,简单来说是SIMD,说实话SIMD我不太懂。没怎么看,不过我觉得是值得一看的

这里的aliasing表述的是多个指针使用指向同一个对象的情况,比如滥用引用,比如自己给自己赋值,之前也提到过误用引用导致错误而引入decay_copy以及c++23的auto,本质上这种问题还是指针的歧义,导致编译器保守了,没有彻底优化

来个代码

void byRef(std::vector<double>& v, const double& coeff) {
  for (auto& i : v) i *= std::sin(coeff);
}

void byVal(std::vector<double>& v, double coeff) {
  for (auto& i : v) i *= std::sin(coeff);
}

注意coeff这种没有必要的const&

再比如

using namespace std;
using namespace std::literals;
template <typename Fun>
void test(string_view name, Fun F) {
    char buffer[50] = "hello ";
    F(buffer + 1, buffer, 6);
    buffer[0] = ' ';
    cout << name << " [" << buffer << "] "
         << (" hello "sv == buffer ? "Good\n" : "Bad\n");
}

void loopcpy(char* dst, const char* src, int size) {
    while (size--) *dst++ = *src++;
}
int main() {
    test("NOP    ", [](auto...) {});
    test("loopcpy", loopcpy);
    test("strcpy ", [](auto dst, auto src, auto...) { strcpy(dst, src); });
    test("strncpy ", strncpy);
    test("memcpy ", memcpy);
    test("memmove", memmove);
    test("copy_n ",
         [](auto dst, auto src, auto size) { copy_n(src, size, dst); });
    return 0;
}

loopcpy这种明显无法区分src dst相同的副作用

在比如

using namespace std;
using namespace std::literals;


int main() {
//    members();
    complex<int> x{2, 2};
    x *= reinterpret_cast<int*>(&x)[0];  // multiply by real part
    cout << "expect (4,4) and get " << x << "\n";
//    lambdas();
    auto add_to_all = [](auto& v, const auto& suffix) {
        for_each(begin(v), end(v), [&](auto& x) { x += suffix; });
    };
    vector<int> v{1, 2, 3};
    add_to_all(v, v[0]);
    cout << "expected [2,3,4] and got [" << v[0] << "," << v[1] << "," << v[2]
         << "]\n";
    return 0;
}

自己改自己以及滥用引用

再比如

int main() {
    auto minmax = [](const string& i, const string& j, string* out_min,
                     string* out_max) {
        *out_min = min(i, j);
        *out_max = max(i, j);
    };
    array<string, 2> arr{"22222", "11111"};
    // try to sort
    minmax(arr[0], arr[1], &arr[0], &arr[1]);
    cout << "expect 22222 and get " << arr[1] << "\n";
    auto concat = [](string& result, const auto&... args) {
        ((result += args), ...);
    };
    string x{"hello "}, y{"world "};
    concat(x, y, x);
    cout << "expect [hello world hello ] and get [" << x << "]\n";
    return 0;
}

这种问题c就有,union都有,但c有__restrict__ c++没有。那么怎么从代码角度避免这种问题?

传值,引用用std::ref,强类型区分

开源项目需要人手

新项目介绍/版本更新

An educational software system of a tiny self-compiling C compiler, a tiny self-executing RISC-V emulator, and a tiny self-hosting RISC-V hypervisor.


本文永久链接

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

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