一些现代cpp问题


// Code from ./9/main.cpp
#include <iostream>
#include <span>
#include <vector>
template <typename T> void print(std::span<T> span) noexcept {
  for (const auto &i : span) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
  std::cout << span.size() << std::endl;
}
int main() {

  std::vector v{1, 2, 3, 4, 5};
  int n[]{1, 2, 3, 4, 5};
  std::vector<std::string> s{"1", "2", "3"};
  print<int>(v);
  print<int>(n);
  print<std::string>(s);
  // NOTE::span只是view开销更小,只保存数据的开始位置和长度
  // NOTE::幽默clangd疯狂报错
  return 0;
}


// Code from ./8/main.cpp
#include <iostream>

int main() {
  double v1 = 1.0, v2 = 2.0;
  auto ret = v1 <=> v2;
  if (ret < 0) {
    std::cout << "v1 < v2" << std::endl;
  } else if (ret > 0) {
    std::cout << "v1 > v2" << std::endl;
  } else {
    std::cout << "v1 = v2" << std::endl;
  }
  // NOTE::幽默clangd居然报错了,g++编译通过
  return 0;
}


// Code from ./1/main.cpp
#include <iostream>
#include <memory>

void f(int, int, int = 10);
void f(int, int = 6, int);
void f(int = 5, int, int);
// void f(int = 6, int = 7, int = 9); NOTE::重定义会报错
// void f(int = 7,int ,int );NOTE::报错原因同上
void f(int a, int b, int c) {
  std::cout << "a=" << a << " b=" << b << " c=" << c << std::endl;
}

void fun(int, int, int = 10); // NOTE::如果没有这一行会报错
void fun(int, int = 6, int);
void fun(int = 5, int, int);
void fun(int a, int b, int c) {
  std::cout << "a=" << a << " b=" << b << " c=" << c << std::endl;
}

class C {
  // void f(int i = 3, int j, int k =
  // 6);//NOTE::报错,只要不是最后一个参数外的其他参数有默认参数,那么所有参数都要加默认参数
  void g(int i, int b,
         int j = 99); // NOTE::没有报错,因为只有最后一个参数有默认构造函数
  C(int arg);
};

class X {
  // void f(int a, int b, int c = 4, int
  // d);//NOTE::报错,第三个参数有默认参数,而其他参数没有
};

// NOTE::非模板类的成员函数类外的定义允许出现默认实参,并与类体内的声明所提供的默认实参组合
//  void C::f(int i=3);NOTE::重定义报错
// void C::f(int i, int j = 5, int k) {}
// void C::g(int i = 88, int j);NOTE::导致在作用域外面声明一个函数,报错
// C::C(int arg = 1) {};//NOTE::报错

struct Base {
  virtual void f(int a = 7) { std::cout << "Base" << a << std::endl; }
};

struct Derived : Base {
  // NOTE::虚函数的覆盖不会从基类定义获得默认实参,而进行虚函数调用时,默认实参根据对象的静态类型确定
  void f(int a) override { std::cout << "Derived" << a << std::endl; }
  // NOTE::输出Derived 7
};

int main() {
  f();

  std::unique_ptr<Base> ptr{new Derived}; // NOTE::这里ptr的静态类型是Base
  ptr->f();
  return 0;
}


// Code from ./2/main.cpp
#include <functional>
#include <iostream>

struct X {
  void f() { std::cout << "6" << std::endl; }
  int num = 0;
};

void f2(void (X::*p)(), X &x) { (x.*p)(); }

int main() {
  void (X::*p)() = &X::f; // NOTE::成员函数指针
  X x;
  (x.*p)(); // NOTE::调用成员函数指针,输出6

  f2(p, x);
  f2(&X::f, x);

  X c;
  int &i = std::invoke(&X::num, &c);
  i = 114545;
  std::cout << c.num << std::endl;
  return 0;
}


// Code from ./11/main.cpp
#include <iostream>

struct X {
  X(const X &) = delete;
  X(int) {}
  X(double) = delete;
};

void f(int) {}
void f(double) = delete;

void f2(std::integral auto) {} // NOTE::这样也可以防止隐式转换
// NOTE::幽默clangd又报错了
int main() {
  f(1);
  // NOTE::f(1.2);报错
  X x(1);
  // NOTE::X *x = new X(1);报错
  return 0;
}


// Code from ./6/main.cpp
#include <iostream>
class X {};

template <typename T> static void f1(T &&x) {
  std::cout << &x << std::endl;
  f2(std::forward<X>(x));
}

template <typename T> static void f2(T &&x) {
  std::cout << &x << std::endl;
  f3(std::forward<X>(x));
}

template <typename T> static void f3(T &&x) {

  std::cout << &x << std::endl;
  f4(std::forward<X>(x));
}

template <typename T> static void f4(T &&x) {
  std::cout << &x << std::endl;
  f5(std::forward<X>(x));
}

template <typename T> static void f5(T &&x) { std::cout << &x << std::endl; }

int main() {
  X x;
  f1(std::move(x));
  // NOTE::两个知识点,c++17强制规定复制消除
  // NOTE::forward将函数接收到的参数以原本的值类别传播,而不是右值引用退化为左值
  return 0;
}


// Code from ./10/main.cpp
#include <iostream>
#include <string>
#include <type_traits>

template <typename T> struct A {
  A(T){};
  T t;
};

template <typename T> A(T) -> A<double>; // NOTE::显式的推导引导

A(const char *) -> A<std::string>; // NOTE::如果推导出了const
                                   // char*,则推导为string

template <typename T, size_t size> struct array {
  T arr[size];
};

template <class Tu, class... Tp>
array(Tu, Tp...) -> array<std::enable_if_t<(std::is_same_v<Tu, Tp> && ...), Tu>,
                          sizeof...(Tp) + 1>;

int main() {
  // NOTE::c++17开始可以类模板实参推导
  // NOTE::就像模板函数一样
  auto t = new A{1};
  // NOTE::分配的类型为A<int>,但因为推导引导,推导为了double

  array arr{1, 2, 3, 4};
  return 0;
}


// Code from ./5/main.cpp
#include <iostream>

int f() { return 6; }

int main() {
  // NOTE::函数返回的是纯右值
  int &&p = f();
  const int &pp = f();

  using T = decltype((42));   // NOTE::int纯右值
  using T2 = decltype((5.2)); // NOTE::double纯右值

  int v{};
  using T3 = decltype((v));                    // NOTE::int&左值表达式
  using T4 = decltype(static_cast<int &&>(v)); // NOTE:: int&& 亡值表达式

  return 0;
}


// Code from ./4/main.cpp
#include <cstring>
#include <iostream>
struct test {
  int a;
  double b;
  char c[0];
  // NOTE::长度为0的数组,即柔性数组
};

int main() {
  auto t = static_cast<test *>(malloc(sizeof(test) + 27 * sizeof(char)));
  memset(t->c, 0, 27);
  for (int i = 0; i < 26; ++i) {
    t->c[i] = 'A' + i;
  }
  std::cout << t->c << std::endl;
  free(t);
  return 0;
}


// Code from ./3/main.cpp
#include <iostream>

int main() {
  static int a = 42;
  auto f = [=]() -> void { ++a; };
  f();
  std::cout << "a=" << a << std::endl;
  std::cout << "sizeof f:" << sizeof f << std::endl;
  // NOTE::输出a=43, sizeof f:1

  const int N = 1145;
  auto p = [=] { int arr[N]; };
  std::cout << "sizeof p" << sizeof p << std::endl;
  // NOTE::输出sizeof p 1,说明根本没捕获N

  auto pp = [=] { int p = N; };
  std::cout << "sizeof pp" << sizeof pp << std::endl;
  // NOTE::gcc输出sizeof p 1,说明根本没捕获N,但msvc会输出4

  return 0;
}


// Code from ./7/main.cpp
#include <iostream>

struct S {
  void f() & { std::cout << "left value" << std::endl; }
  void f() && { std::cout << "r value" << std::endl; }
};

int main() {
  S s;
  s.f();            // NOTE::left value
  std::move(s).f(); // NOTE:: right value
  S().f();          // NOTE:: right value
  return 0;
}