C++17新特性 - 知乎 (zhihu.com)
C++17 - cppreference.com
为了实例化一个类模板,需要知晓但不需要指定每个模板实参。编译器会从初始化器的类型推导缺失的模板实参。
template
struct A {
T t;
U u;
A(T _t, U _u) : t(_t), u(_u) {}
};
int main(int argc, char** argv) {
std::pair p(2, 4.5); // td::pair p(2, 4.5);
A a(3.14159265, "hello world"); // double, string
std::cout << a.t << ", " << a.u << std::endl;
// 3.14159, hello world
return 0;
}
绑定指定名称到初始化器的子对象或元素。
类似引用,结构化绑定是既存对象的别名。不同于引用的是,结构化绑定的类型不必为引用类型。
// 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] = 表达式 ; (1)
// 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] { 表达式 } ; (2)
// 属性(可选) cv-auto 引用运算符(可选) [ 标识符列表 ] ( 表达式 ) ; (3)
int a[2] = {3, 6};
// creates e[2], copies a into e,
// then x refers to e[0], y refers to e[1]
auto [x, y] = a;
// xr refers to a[0], yr refers to a[1]
auto& [xr, yr] = a;
std::cout << x << ", " << y << std::endl; // 3, 6
std::cout << a[0] << ", " << a[1] << std::endl; // 3, 6
std::cout << xr << ", " << yr << std::endl; // 3, 6
xr = 999; yr = -99;
std::cout << a[0] << ", " << a[1] << std::endl; // 999, -99
std::cout << xr << ", " << yr << std::endl; // 999, -99
float x = 3.1415f;
char y = 'w';
int z = 996;
std::tuple tpl(x, std::move(y), z);
const auto& [a, b, c] = tpl;
std::cout << a << ", " << b << ", " << c << std::endl;
// 3.1415, w, 996
struct S {
mutable int x = 2;
volatile double y;
};
S s;
const auto [x, y] = s;
std::cout << x << ", " << y << std::endl;
std::cout << s.x << ", " << s.y << std::endl;
// 2, 4.63895e-310
// 2, 4.63895e-310
语法
( 形参包 运算符 ... ) (1)
( ... 运算符 形参包 ) (2)
( 形参包 运算符 ... 运算符 初值 ) (3)
( 初值 运算符 ... 运算符 形参包 ) (4)
// fold expression (C++17)
#include
template
bool all(Args... args) {
// ( ... 运算符 形参包 )
// 一元左折叠
return (... && args);
}
template
auto sum(Args... args) {
// ( 形参包 运算符 ... )
// 一元右折叠
return (args + ...);
}
int main(int argc, char** argv) {
std::cout << all(true, true, true, false) << std::endl; // 0
std::cout << sum(true, true, true, false) << std::endl; // 3
std::cout << sum(std::string("hello"), std::string(" "), std::string("world")) << std::endl;
// hello world
return 0;
}
C++17前lambda表达式只能在运行时使用,C++17引入了constexpr lambda表达式,可以用于在编译期进行计算。
int main(int argc, char** argv) {
constexpr auto f = [](int n) { return n * n; };
static_assert(f(3) == 9, "error");
return 0;
}
namespace A {
namespace B {
namespace C {
void f () {
std::cout << "hello world" << std::endl;
}
}
}
}
int main(int argc, char** argv) {
A::B::C::f();
return 0;
}
// hello world
将其他源文件包含到当前源文件中紧随指令之后的一行。
结果是 1 的 __has_include
只表明存在有指定名称的头或源文件。它并不意味着包含该头或源文件时不会导致错误,或它会包含任何有意义的内容。
#if __has_include()
#include
#define has_iostream 1
#endif
在if和switch语句中加入初始化语句部分,增加代码的简洁性。
if (int a = get_value(); a < 10) {
std::cout << a << std::endl;
}
// 5
std::string str = "Hello world";
if (auto [pos, size] = std::pair(str.find("Hello"), str.size());
pos != std::string::npos) {
std::cout << pos << ", " << size << std::endl;
}
// 0, 11
内联变量消除了将 C++ 代码打包为唯头文件的库的主要障碍。
拥有外部链接的包含于多个源文件的变量必须为 inline
// header file
struct A {
static const int value;
};
inline int const A::value = 10;
// ==========或者========
struct A {
inline static const int value = 10;
}
当前对象的简单的以复制捕获
struct A {
int a;
void fun() {
// 持有对象的拷贝
[*this] { std::cout << a << std::endl; } ();
}
};
int main(int argc, char** argv) {
A a{314};
a.fun();
return 0;
}
[[nodiscard]]和
[[maybe_unused]]C++ 标准仅定义下列属性。
[[noreturn]] (C++11) |
指示函数不返回 |
---|---|
[[carries_dependency]] (C++11) |
指示释放消费 std::memory_order 中的依赖链传入和传出该函数。 |
[[deprecated]] (C++14) [[deprecated("原因")]] (C++14) |
指示允许使用声明有此属性的名称或实体,但因 原因 而不鼓励使用。 |
[[fallthrough]] (C++17) |
指示从前一 case 标号直落是有意的,而在发生直落时给出警告的编译器不应该为此诊断。 |
[[nodiscard]] (C++17) |
鼓励编译器在返回值被舍弃时发布警告。 |
[[maybe_unused]] (C++17) |
压制编译器在未使用实体上的警告,若存在。 |
#include
void fun(int x) {
switch (x) {
case 0:
std::cout << "x = 0" << std::endl;
case 1:
std::cout << "x = 1" << std::endl;
[[fallthrough]]; // 直落时不警告
default:
std::cout << "x >= 0" << std::endl;
}
}
[[nodiscard]] int g(int x) { return x + 6; }
// 提示编译器修饰的内容可能暂时没有使用,避免产生警告
[[maybe_unused]] int h(int x) { return x + 2; }
int main(int argc, char** argv) {
fun(1);
g(3); // warning: ignoring return value of ‘int g(int)’
return 0;
}
类 | |
---|---|
chars_format(C++17) | 指定 std::to_chars 和 std::from_chars 所用的格式 (枚举) |
函数 | |
from_chars(C++17) | 转换字符序列到整数或浮点值 (函数) |
to_chars(C++17) | 转换整数或浮点值到字符序列 (函数) |
#include
#include
int main(int argc, char** argv) {
const std::string pi_str{"314159265"};
int value = 0;
std::from_chars(pi_str.data(), pi_str.data() + pi_str.size(), value);
std::cout << sizeof(pi_str) << ", " << sizeof(value) << std::endl;
// 32, 4
std::string val_str = std::string("xxxx");
const int v = 1234;
std::to_chars(val_str.data(), val_str.data() + val_str.size(), v);
std::cout << val_str << std::endl; // 1234
std::cout << sizeof(val_str) << ", " << sizeof(v) << std::endl;
// 32, 4
}
std::variant实现类似union的功能,但却比union更高级,举个例子union里面不能有string这种类型,但std::variant却可以,还可以支持更多复杂类型,如map等
#include
#include
using std::cout;
using std::endl;
struct A {
A () = default;
A(int i){}
};
int main(int argc, char** argv) {
std::variant var("hello");
cout << var.index() << ", " << std::get<1>(var) << endl;
var = 123;
cout << var.index() << ", " << std::get<0>(var) << endl;
// variant的第一个类型一般要有对应的构造函数,否则编译失败
std::variant var_tmp;
return 0;
}
// 1, hello
// 0, 123
可以使用std::monostate来打个桩,模拟一个空状态。
monostate用作非可默认构造类型的
variant
的首个可选项的占位符类型
std::optional可用于正常值和异常值的统一返回
#include
#include
#include
using std::cout;
using std::endl;
std::optional my_stoi(const std::string &s) {
try {
return std::stoi(s);
} catch (...) {
// `nullopt_t` 类型对象 (常量)
return std::nullopt;
}
}
int* my_stoi_2(const std::string &s) {
try {
return &(std::stoi(s));
} catch (...) {
return nullptr;
}
}
int main(int argc, char** argv) {
std::string s {"xaba"};
std::optional o = my_stoi(s);
if (o) {
cout << *o << endl;
} else {
cout << "error" << endl;
}
return 0;
}
// error
可保有任何可复制构造 (CopyConstructible) 类型的实例的对象。
函数 | |
---|---|
std::swap(std::any)(C++17) | 特化 std::swap 算法 (函数) |
make_any(C++17) | 创建 any 对象 (函数模板) |
any_cast(C++17) | 对被容纳对象的类型安全访问 (函数模板) |
#include
#include
using std::cout;
using std::endl;
template
void print_any(const std::any& a) {
if (a.has_value()) {
cout << a.type().name() << ", " << std::any_cast(a) << endl;
}
}
int main(int argc, char** argv) {
std::any a = 3.14159265;
print_any(a);
a = std::string{"abcdef"};
print_any(a);
return 0;
}
// d, 3.14159
// NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, abcdef
定义于头文件
template constexpr decltype(auto) apply(F&& f, Tuple&& t);
// f - 要调用的可调用 (Callable) 对象
// t - 以其元素为 f 的参数的元组
#include
#include
using std::cout;
using std::endl;
template
T add_fun_template(const T& a, const T& b) { return a + b; }
auto add_lambda = [](const auto a, const auto b) { return a + b; };
int main(int argc, char** argv) {
// error : 无法推导函数类型
// // cout << std::apply(add_fun_template, std::pair(2.0f, 3.0f)) << endl;
// cout << std::apply(add_fun_template, std::pair(2.0f, 3.0f)) << endl;
// OK : lambda模板
cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << endl;
cout << std::apply(add_lambda, std::pair("2.0f", "3.0f")) << endl;
return 0;
}
直接打印tuple
template
std::ostream& operator<<(std::ostream& os, std::tuple const& theTuple) {
std::apply (
[&os](Ts const&... tupleArgs) {
os << '[';
std::size_t n{0};
((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
os << ']';
}, theTuple
);
return os;
}
// 直接打印tuple
cout << std::tuple(25, "Hello", 9.31f, 'c') << endl;
定义于头文件
template
constexpr T make_from_tuple(Tuple&& t);
// 注意t是右引用值
#include
#include
struct Foo {
Foo(int first, float second, int third) {
std::cout << first << ", " << second << ", " << third << "\n";
}
};
int main()
{
auto tuple = std::make_tuple(42, 3.14f, 0);
std::make_from_tuple(std::move(tuple));
}
// 42, 3.14, 0
此头文件是字符串库的一部分。
std::string_view来获取一个字符串的视图,字符串视图并不真正的创建或者拷贝字符串,而只是拥有一个字符串的查看功能。std
避免传递一个string对象引发拷贝构造
#include
#include
#include
using std::cout;
using std::endl;
void print_str(std::string_view stv) {
cout << stv << endl;
}
int main(int argc, char** argv) {
std::string str = "hello world";
cout << str << endl;
print_str(str);
return 0;
}
// hello world
// hello world
定义于头文件
// 将左值引用组成 t 的 const 类型
template
constexpr std::add_const_t& as_const(T& t) noexcept;
// 删除 const 右值引用重载,以禁止右值参数
template
void as_const(const T&&) = delete;
可能的实现
template
constexpr std::add_const_t& as_const(T& t) noexcept
{
return t;
}
std::string mutableString = "Hello World!";
auto&& constRef = std::as_const(mutableString);