C++ 动态新闻推送 第 36 期
- 2021 年 11 月 12 日
- 本文字数:14060 字 - 阅读完需:约 46 分钟 
从reddit/hackernews/lobsters/meetingcpp摘抄一些 c++动态
每周更新
欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue
cppcon2021 开完了,陆续把视频发出来,有几个更几个
meetingcpp 这周也开始了,也是一周,所以这个十一月视频多的要死看不过来
想直接看的直接往下拉到视频环节
资讯
编译器信息最新动态推荐关注 hellogcc 公众号
文章
还是 concept,复读几遍就记住了,这里在啰嗦一下
#include <concepts>#include <iostream> template<std::integral T>void overloaded(T a){   std::cout << "Integral overload called with " << a << std::endl;} template<std::integral T> requires (sizeof(T) == 2)void overloaded(T a){  std::cout << "Short overload called with " << a << std::endl;} int main(){  int a{10};  short b{20};  overloaded(a);  overloaded(b);}
你学会了吗~
又一个 range 教程,复读几遍读者就记住了,这里在啰嗦一下
替代 stl
std::vector<int> dt = {1, 4, 2, 3};std::ranges::sort(dt);
映射
struct Account {    std::string owner;    double value();    double base();};std::vector<Account> acc = get_accounts();// memberstd::ranges::sort(acc,{},&Account::owner);// member functionstd::ranges::sort(acc,{},&Account::value);// lambdastd::ranges::sort(acc,{},[](const auto& a) {     return a.value()+a.base(); });
view
namespace rv = std::ranges::views;std::vector<int> dt = {1, 2, 3, 4, 5, 6, 7};for (int v : rv::reverse(rv::take(rv::reverse(dt),3))) {    std::cout << v << ", ";}std::cout << "\n";
你学会了吗~
python 有 GIL 大锁,最近社区突然有个牛人实现了一个去掉 GIL 的 python,性能有提升,单核 9%
这篇文章介绍一些相关的信息
nogil 版本从 3.9 拉出来的,现在已经 3.11 了,一时半会用不上,代码可能需要很久
3.11 也有很多性能优化的提升,nogil 版本比 3.9 快 ,但没有 3.11 快 (单核) 更细致的比较数据暂时没有
使用 mimalloc 替换内部 pymalloc mimalloc 在大量小对象场景上有很好的提升效果,且 mimalloc 本身的设计,竞争轻量
使用biased reference counting 降低引用计数的开销 论文在这
对象和自身的线程绑定
本地线程访问对象无需原子访问
其他线程访问还有有引用计数的增减的
一些对象就不计数了; 比如 None, True, False, 内部字符串,小数字等等, PyTypeObjects for built-in types
一些对象就推迟计算引用计数了,module 代码块这些,可能和程序的生命周期相同
这里面 mimalloc 带来的提升很有效果,且 mimalloc 也有合作帮助优化 cpython,这个 mimalloc 值得研究研究
另外,微软又出了个 snmalloc,也有一些 mimalloc 的设计,也值得研究研究
手把手教你用 goto
多层 break 场景
    for (int i = 0; i < 3; i++) {        for (int j = 0; j < 3; j++) {            for (int k = 0; k < 3; k++) {                printf("%d, %d, %d\n", i, j, k);
                goto continue_i;   // Now continuing the i loop!!            }        }continue_i: ;    }
这种场景下,break continue 都无法替代 (或者别这么写代码)
错误处理场景
   for(...) {        for (...) {            while (...) {                do {                    if (some_error_condition)                        goto bail;
                } while(...);            }        }    }
bail:
还有一种, 逐层清理 (c++有 RAII 处理这个)
    if (init_system_1() == -1)        goto shutdown;
    if (init_system_2() == -1)        goto shutdown_1;
    if (init_system_3() == -1)        goto shutdown_2;
    if (init_system_4() == -1)        goto shutdown_3;
    do_main_thing();   // Run our program
    shutdown_system4();
shutdown_3:    shutdown_system3();
shutdown_2:    shutdown_system2();
shutdown_1:    shutdown_system1();
shutdown:    print("All subsystems shut down.\n");
retry 循环
retry:    byte_count = read(0, buf, sizeof(buf) - 1);  // Unix read() syscall
    if (byte_count == -1) {            // An error occurred...        if (errno == EINTR) {          // But it was just interrupted            printf("Restarting...\n");            goto retry;        }
while 也可以
坑爹场景,goto 和局部变量处理,局部变量生命周期要在 goto 的 lable 后可见
template<class, std::size_t> concept Any = true;
constexpr auto last1 = [](auto... args) {  return [&]<std::size_t... Ns>(std::index_sequence<Ns...>) {    return [](Any<Ns> auto..., auto last) {      return last;    }(args...);  }  (std::make_index_sequence<sizeof...(args) - 1>{});};
auto last2 = [](auto... args) {  return (args, ...);};
static_assert(1 == last1(1));static_assert(2 == last1(1, 2));static_assert(3 == last1(1, 2, 3));
static_assert(1 == last2(1));static_assert(2 == last2(1, 2));static_assert(3 == last2(1, 2, 3))
这个 last2 有点意思,不是新的东西 c++17 也能编的过
吐槽 coroutine 难用,比如不是零开销抽象,api 难用太低层之类的
一种代码 api 风格转换,套上观察者模式,代码在这里,可以看看
一个测试,malloc 并没有返回报错而是直接崩溃了?
#include <stdio.h>#include <stdlib.h>
int main() {  size_t large = 1099511627776;  char *buffer = (char *)malloc(large);  if (buffer == NULL) {    printf("error!\n");    return EXIT_FAILURE;  }  printf("Memory allocated\n");  for (size_t i = 0; i < large; i += 4096) {    buffer[i] = 0;  }  free(buffer);  return EXIT_SUCCESS;}
分配的内存是虚拟内存,不是物理内存,直接访问,访问出 segfault 了,真奇怪
视频
auto 可以用 concept 来修饰,从而实现限制 auto。想要让 auto 在指定的 concept 下面 auto
讲设计原理的。。。
cppcon 结束了,jetbrains 作为赞助商提前放出来了一些视频 这里简单过一下
这个之前说过,就是 c++引入模式匹配,引入 is as 关键字
void f(auto const& x) {  inspect (x) {    i as int           => std::cout << "int " << i;    [_,y] is [0,even]  => std::cout << "point on y-axis and even y " << y;    [a,b] is [int,int] => std::cout << "2-int tuple " << a << " " << b;    s as std::string   => std::cout << "string \"" + s + "\"";    is _               => std::cout << "((no matching value))";  }}
int main() {  f(42);  f(std::pair{0, 2});  f(std::tuple{1, 2});  f("str");  struct {} foo;  f(foo);}
和 std::find 没关系啊,这个就是讨论语义的,条件,限制之类的,Sean Paren 大哥讲故事,还讲了几个冷笑话
没什么意思,讲语义的。给我听困了
单片机越来越廉价普及,应该大力发挥 c++在其中的作用。然后说了一大堆概念。怎么都这么抽象
项目
- Solarflare网卡的用户有福了:efvitcp上线! 作者实现基于 efvi 上的 tcp,代码在这里 
- observable_unique_ptr<T, Deleter> 把 shared_ptr -weak_ptr 这种设计搬迁到 unique_ptr 上,有点意思 
- Cpp Dyn 一个多态设计,可以让内存分配更精细,而不是单纯的 new 实现用了一堆宏。原理没研究 
- captal-engine 一个 2D 游戏引擎 
- luau 一个 lua c++实现,fork 的 lua 5.1,但是用 c++写了大部分 
之前也说过这个思路,fixed_string + UDL
#include <algorithm>#include <any>#include <experimental/iterator>#include <iostream>#include <iterator>#include <string_view>#include <tuple>#include <type_traits>#include <utility>#include <vector>
template <auto Size>struct fixed_string {  char data[Size + 1]{};  static constexpr auto size = Size;
  constexpr explicit(false) fixed_string(char const* str) {    std::copy_n(str, Size + 1, data);  }  constexpr explicit(false) operator std::string_view() const {    return {data, Size};  }};template <auto Size>fixed_string(char const (&)[Size]) -> fixed_string<Size - 1>;
using std::literals::string_view_literals::operator""sv;
static_assert(""sv == fixed_string(""));static_assert("name"sv == fixed_string("name"));
template <fixed_string Name, class TValue>struct arg {  static constexpr auto name = Name;  TValue value{};  template <class T>  constexpr auto operator=(const T& t) {    return arg<Name, T>{.value = t};  }};
namespace detail {template <class TDefault, fixed_string, template <fixed_string, class> class>auto map_lookup(...) -> TDefault;template <class, fixed_string TKey, template <fixed_string, class> class TArg,          class TValue>auto map_lookup(TArg<TKey, TValue>*) -> TArg<TKey, TValue>;
template <class TDefault, class, template <class, class> class>auto map_lookup(...) -> TDefault;template <class, class TKey, template <class, class> class TArg, class TValue>auto map_lookup(TArg<TKey, TValue>*) -> TArg<TKey, TValue>;}  // namespace detail
template <class T, fixed_string TKey, class TDefault,          template <fixed_string, class> class TArg>using map_lookup = decltype(detail::map_lookup<TDefault, TKey, TArg>(    static_cast<T*>(nullptr)));
template <class... Ts>struct inherit : Ts... {};
static_assert(std::is_same_v<              void, map_lookup<inherit<arg<"price", double>, arg<"size", int>>,                               "unknown", void, arg>>);static_assert(    std::is_same_v<arg<"price", double>,                   map_lookup<inherit<arg<"price", double>, arg<"size", int>>,                              "price", void, arg>>);static_assert(    std::is_same_v<arg<"size", int>,                   map_lookup<inherit<arg<"price", double>, arg<"size", int>>,                              "size", void, arg>>);
struct any : std::any {  any() = default;  template <class T>  explicit(false) any(const T& a)      : std::any{a},        print{[](std::ostream& os, const std::any& a) -> std::ostream& {          if constexpr (requires { os << std::any_cast<T>(a); }) {            os << std::any_cast<T>(a);          } else if constexpr (requires {                                 std::begin(std::any_cast<T>(a));                                 std::end(std::any_cast<T>(a));                               }) {            auto obj = std::any_cast<T>(a);            std::copy(std::begin(obj), std::end(obj),                      std::experimental::make_ostream_joiner(os, ','));          } else {            os << a.type().name();          }          return os;        }} {}  template <class T>  constexpr explicit(false) operator T() const {    return std::any_cast<T>(*this);  }
  friend std::ostream& operator<<(std::ostream& os, const any& a) {    return a.print(os, a);  }
 private:  std::ostream& (*print)(std::ostream&, const std::any&){};};
template <fixed_string Name>constexpr auto operator""_t() {  return arg<Name, any>{};}
template <class T, class... TArgs>decltype(void(T{std::declval<TArgs>()...}), std::true_type{})    test_is_braces_constructible(int);template <class, class...>std::false_type test_is_braces_constructible(...);template <class T, class... TArgs>using is_braces_constructible =    decltype(test_is_braces_constructible<T, TArgs...>(0));
struct any_type {  template <class T>  constexpr operator T();  // non explicit};
template <class T>constexpr auto to_tuple(T object) noexcept {  using type = std::decay_t<T>;  if constexpr (is_braces_constructible<type, any_type, any_type, any_type,                                        any_type, any_type, any_type, any_type,                                        any_type, any_type, any_type>{}) {    auto [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);  } else if constexpr (is_braces_constructible<type, any_type, any_type,                                               any_type, any_type, any_type,                                               any_type, any_type, any_type,                                               any_type>{}) {    auto [p1, p2, p3, p4, p5, p6, p7, p8, p9] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5, p6, p7, p8, p9);  } else if constexpr (is_braces_constructible<                           type, any_type, any_type, any_type, any_type,                           any_type, any_type, any_type, any_type>{}) {    auto [p1, p2, p3, p4, p5, p6, p7, p8] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5, p6, p7, p8);  } else if constexpr (is_braces_constructible<type, any_type, any_type,                                               any_type, any_type, any_type,                                               any_type, any_type>{}) {    auto [p1, p2, p3, p4, p5, p6, p7] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5, p6, p7);  } else if constexpr (is_braces_constructible<type, any_type, any_type,                                               any_type, any_type, any_type,                                               any_type>{}) {    auto [p1, p2, p3, p4, p5, p6] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5, p6);  } else if constexpr (is_braces_constructible<type, any_type, any_type,                                               any_type, any_type,                                               any_type>{}) {    auto [p1, p2, p3, p4, p5] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4, p5);  }  if constexpr (is_braces_constructible<type, any_type, any_type, any_type,                                        any_type>{}) {    auto [p1, p2, p3, p4] = std::forward<T>(object);    return std::tuple(p1, p2, p3, p4);  } else if constexpr (is_braces_constructible<type, any_type, any_type,                                               any_type>{}) {    auto [p1, p2, p3] = std::forward<T>(object);    return std::tuple(p1, p2, p3);  } else if constexpr (is_braces_constructible<type, any_type, any_type>{}) {    auto [p1, p2] = std::forward<T>(object);    return std::tuple(p1, p2);  } else if constexpr (is_braces_constructible<type, any_type>{}) {    auto [p1] = std::forward<T>(object);    return std::tuple(p1);  } else {    return std::tuple{};  }}
// static_assert("name"sv == ("name"_t = 42).name);// static_assert(42       == ("name"_t = 42).value);template <class T>[[nodiscard]] constexpr auto type_name() -> std::string_view {#if defined(_MSC_VER) and not defined(__clang__)  return {&__FUNCSIG__[120], sizeof(__FUNCSIG__) - 128};#elif defined(__clang_analyzer__)  return {&__PRETTY_FUNCTION__[57], sizeof(__PRETTY_FUNCTION__) - 59};#elif defined(__clang__) and (__clang_major__ >= 12) and not defined(__APPLE__)  return {&__PRETTY_FUNCTION__[34], sizeof(__PRETTY_FUNCTION__) - 36};#elif defined(__clang__)  return {&__PRETTY_FUNCTION__[70], sizeof(__PRETTY_FUNCTION__) - 72};#elif defined(__GNUC__)  return {&__PRETTY_FUNCTION__[85], sizeof(__PRETTY_FUNCTION__) - 136};#endif}
namespace nt {template <class B, class... Ts>struct namedtuple : B, private Ts... {  static constexpr auto name_v = [] {    if constexpr (requires { B::name; }) {      return B::name;    } else {      return type_name<B>();    }  }();  static inline std::vector<std::string_view> names;
  constexpr explicit(true) namedtuple(Ts... ts)      : B{[=] {          if constexpr (requires { B{ts.value...}; }) {            return B{ts.value...};          } else {            return B{};          }        }()},        Ts{ts}... {    names = {ts.name...};  }
  template <class B_, class... Ts_>  auto& operator=(const namedtuple<B_, Ts_...>& other) {    names = {Ts_::name...};    static_cast<B&>(*this) = static_cast<const B&>(other);    return *this;  }
  template <class T, class TArg = map_lookup<namedtuple, T::name, void, arg>>  constexpr const auto& operator[](const T) const      requires(not std::is_void_v<TArg>) {    return static_cast<const TArg&>(*this).value;  }
  template <class T, class TArg = map_lookup<namedtuple, T::name, void, arg>>  constexpr auto& operator[](const T) requires(not std::is_void_v<TArg>) {    return static_cast<TArg&>(*this).value;  }
  auto& assign(auto&&... ts) {    if constexpr ((requires {                    ts.name;                    ts.value;                  } and                   ...)) {      ((static_cast<decltype(ts)&>(*this) = ts), ...);    } else {      ((static_cast<Ts&>(*this).value = ts), ...);    }    return *this;  }
  template <std::size_t N>  auto& get() {    auto id_type = []<auto... Ns>(std::index_sequence<Ns...>) {      return inherit<          std::pair<std::integral_constant<std::size_t, Ns>, Ts>...>{};    }    (std::make_index_sequence<sizeof...(Ts)>{});    return static_cast<        typename decltype(detail::map_lookup<                          void, std::integral_constant<std::size_t, N>,                          std::pair>(&id_type))::second_type&>(*this);  }
  template <std::size_t N>  const auto& get() const {    auto id_type = []<auto... Ns>(std::index_sequence<Ns...>) {      return inherit<          std::pair<std::integral_constant<std::size_t, Ns>, Ts>...>{};    }    (std::make_index_sequence<sizeof...(Ts)>{});    return static_cast<        const typename decltype(detail::map_lookup<                                void, std::integral_constant<std::size_t, N>,                                std::pair>(&id_type))::second_type&>(*this);  }
  friend std::ostream& operator<<(std::ostream& os,                                  const namedtuple& nt) requires(sizeof...(Ts) >                                                                 0) {    os << std::string_view{name_v} << '{';    [&]<auto... Ns>(std::index_sequence<Ns...>) {      ((os << (Ns ? "," : "") << std::string_view{Ts::name} << ':'           << static_cast<const map_lookup<namedtuple, Ts::name, void, arg>&>(                  nt)                  .value),       ...);    }    (std::make_index_sequence<sizeof...(Ts)>{});    os << '}';    return os;  }
  friend std::ostream& operator<<(      std::ostream& os, const namedtuple& nt) requires(sizeof...(Ts) == 0) {    os << std::string_view{name_v} << '{';    auto t = to_tuple(static_cast<const B&>(nt));    [&]<auto... Ns>(std::index_sequence<Ns...>) {      ((os << (Ns ? "," : "") << std::string_view{nt.names[Ns]} << ':'           << std::get<Ns>(t)),       ...);    }    (std::make_index_sequence<std::tuple_size_v<decltype(t)>>{});    os << '}';    return os;  }};template <class... Ts>namedtuple(Ts...) -> namedtuple<void, Ts...>;}  // namespace nt
template <class T, class... Ts>constexpr auto namedtuple(Ts... ts) {  return nt::namedtuple<T, Ts...>(ts...);}
template <fixed_string Name, class... Ts>constexpr auto namedtuple(Ts... ts) {  return nt::namedtuple<arg<Name, std::any>, Ts...>(ts...);}
int main() {  // namedtuple in-place  const auto nt1 = namedtuple<"s">("price"_t = 42., "size"_t = 100);  std::cout << nt1["price"_t] << ',' << nt1["size"_t] << '\n' << nt1 << '\n';
  // namedtuple in-place/struct  struct s {    double price;    int size;  };  const auto nt2 = namedtuple<s>("price"_t = 42., "size"_t = 100);  std::cout << nt2["price"_t] << ',' << nt2["size"_t] << '\n'            << nt2.price << ',' << nt2.size << '\n'            << nt2 << '\n';
  // namedtuple struct  nt::namedtuple<s> nt3;  nt3 = namedtuple<s>("price"_t = 42., "size"_t = 100);  std::cout << nt3.price << ',' << nt3.size << '\n' << nt3 << '\n';}
也直接贴代码
// Pretty-printer for `struct` layout and padding bytes// by Vittorio Romeo (@supahvee1234) (https://vittorioromeo.info)
#include <boost/pfr.hpp>#include <iostream>#include <tuple>#include <utility>#include <type_traits>#include <typeinfo>#include <cmath>#include <iomanip>#include <cstring>#include <array>
namespace detail{       // ------------------------------------------------------------------------------    // Round `x` up to the nearest multiple of `mult`.    [[nodiscard]] constexpr std::size_t round_up(const std::size_t x,                                                  const std::size_t mult) noexcept    {        return ((x + mult - 1) / mult) * mult;    }
    // ------------------------------------------------------------------------------    // Recursively print the memory layout of `T` using identation `indent` and    // keeping track of the total occupied bytes in `used`.    template <typename T>    void print_layout_impl(const std::size_t indent, std::size_t& used)    {        // ------------------------------------------------------------------------------        // Utilities.        const char* const t_name = typeid(T).name();        const std::size_t line_length = std::strlen(t_name) + 32;
        const auto print_indent = [&](const std::size_t spacing = 1)        {            for (std::size_t i = 0; i <= indent; ++i) { std::cout << ((i % 2 == 0) ? '|' : ' ');  }            for (std::size_t i = 0; i < spacing; ++i) { std::cout << ' ';  }        };
        const auto print_line = [&]        {            print_indent(0);            for (std::size_t i = 0; i < line_length; ++i) { std::cout << '-'; }            std::cout << '\n';        };
        // ------------------------------------------------------------------------------        // Tuple type of all data members of `T`, in order. Used to reflect on `T`.        using tuple_type = decltype(boost::pfr::structure_to_tuple(std::declval<T>()));
        // ------------------------------------------------------------------------------        // We use a pointer to `std::tuple` to support non-default-constructible types.        // We need this inner lambda so that we can use `Ts...` as a pack.        [&]<typename... Ts>(std::tuple<Ts...>*)        {               // ------------------------------------------------------------------------------            // All alignments of the data members, with the alignment of `T` at the end.            constexpr std::array alignments{alignof(Ts)..., alignof(T)};
            // ------------------------------------------------------------------------------            // Information printed in the header.            constexpr std::size_t sum_of_member_sizes = (sizeof(Ts) + ...);            constexpr std::size_t total_padding_bytes = (sizeof(T) - sum_of_member_sizes);
            // ------------------------------------------------------------------------------            // Print the header.            print_line();            print_indent();                        std::cout << t_name                       << " {size: " << sizeof(T) << " ("                       << sum_of_member_sizes << "# " << total_padding_bytes                       << "p), align: " << alignof(T) << "}\n";
            print_line();
            std::size_t type_idx = 0;
            // -----------------------------------------------------------------------------            // Print padding in relation to a given alignment            const auto print_padding = [&](const std::size_t alignment)            {                const std::size_t padding = round_up(used, alignment) - used;                for (int i = 0; i < padding; ++i) { std::cout << 'p'; }                            used += padding;            };
            // -----------------------------------------------------------------------------            // Non-recursively print a fundamental/pointer/reference type            const auto print_fundamental = [&]<typename X>            {                print_indent();                std::cout << std::setw(2) << used << ": [";
                print_padding(alignments[type_idx]); 
                for (std::size_t i = 0; i < sizeof(X); ++i) { std::cout << '#'; }                used += sizeof(X);                                print_padding(alignments[type_idx + 1]);
                std::cout << "] " << typeid(X).name() << '\n';            };
            // -----------------------------------------------------------------------------            // Recursively print all data members            ([&]            {                if constexpr(std::is_fundamental_v<Ts>                          || std::is_pointer_v<Ts>                          || std::is_reference_v<Ts>)                {                    print_fundamental.template operator()<Ts>();                   }                else                {                    print_layout_impl<Ts>(indent + 2, used);                }
                ++type_idx;            }(), ...);        }(static_cast<tuple_type*>(nullptr));
        print_line();    }}
template <typename T>void print_layout(){    std::size_t used = 0;    detail::print_layout_impl<T>(0 /* indent */, used /* used */);}
int main(){    struct foo1     {        char *p;     /* 8 bytes */        char c;      /* 1 byte        char pad[7];    7 bytes */        long x;      /* 8 bytes */    };
    print_layout<foo1>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo2     {        char  c;      /* 1 byte         char  pad[7];    7 bytes */        char* p;      /* 8 bytes */        long  x;      /* 8 bytes */    };
    print_layout<foo2>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo3     {        char* p;      /* 8 bytes */        char  c;      /* 1 byte         char  pad[7];    7 bytes */    };
    print_layout<foo3>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo4     {        short s;      /* 2 bytes */        char  c;      /* 1 byte         char  pad[1];    1 byte */    };
    print_layout<foo4>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo5     {        char c;           /* 1 byte        char pad1[7];        7 bytes */
        struct foo5_inner         {            char* p;       /* 8 bytes */            short x;       /* 2 bytes             char  pad2[6];    6 bytes */        } inner;    };
    print_layout<foo5::foo5_inner>();    std::cout << '\n';
    print_layout<foo5>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo10     {        char   c;       /* 1 byte         char   pad1[7];    7 bytes */        foo10* p;       /* 8 bytes */        short  x;       /* 2 bytes         char   pad2[6];    6 bytes */    };
    print_layout<foo10>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct foo11     {        foo11* p;      /* 8 bytes */        short  x;      /* 2 bytes */        char   c;      /* 1 byte         char   pad[5];    5 bytes */    };
    print_layout<foo11>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct test0    {        int   i;        char  c;        float f;    };
    print_layout<test0>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct test1    {        int    i;        double d;        char   c;        float  f;    };
    print_layout<test1>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct test2    {        void* p0;        test0 t0;        void* p1;        test1 t1;        void* p2;    };        print_layout<test2>();    std::cout << '\n';
    // ------------------------------------------------------------------------------
    struct test2_flat    {        void*  p0;        int    i0;        char   c0;        float  f0;        void*  p1;        int    i1;        double d0;        char   c1;        float  f1;        void*  p2;    };        print_layout<test2_flat>();    std::cout << '\n';}
看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持!
版权声明: 本文为 InfoQ 作者【很水】的原创文章。
原文链接:【http://xie.infoq.cn/article/da991a8802d19c16ba9cae51d】。文章转载请联系作者。

很水
还未添加个人签名 2020.10.21 加入
还未添加个人简介











 
    
评论