C++ 中文周刊 第36期

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

每周更新

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

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

cppcon2021开完了,陆续把视频发出来,有几个更几个

meetingcpp这周也开始了,也是一周,所以这个十一月视频多的要死看不过来

想直接看的直接往下拉到视频环节


资讯

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

OSDT Weekly 2021-11-03 第122期

文章

还是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();
// member
std::ranges::sort(acc,{},&Account::owner);
// member function
std::ranges::sort(acc,{},&Account::value);
// lambda
std::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%

这篇文章介绍一些相关的信息

这里面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也能编的过

godbolt

吐槽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了,真奇怪

process memory (virtual) memory (real)
qemu 3.94 GB 32 MB
safari 3.7 GB 180 MB

视频

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++在其中的作用。然后说了一大堆概念。怎么都这么抽象

项目

之前也说过这个思路,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';
}

本文永久链接

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