Skip to content

Commit 1a8e72a

Browse files
committed
more tricks
1 parent c3794cf commit 1a8e72a

File tree

2 files changed

+96
-5
lines changed

2 files changed

+96
-5
lines changed

docs/cpp_tricks.md

+76-5
Original file line numberDiff line numberDiff line change
@@ -83,14 +83,22 @@ std::wstring utf8_to_wstring(std::string const &s) {
8383
## 读取整个文件到字符串
8484

8585
```cpp
86-
TODO
86+
std::string file_get_content(std::string const &filename) {
87+
std::ifstream ifs(filename, std::ios::in | std::ios::binary);
88+
std::istreambuf_iterator<char> iit(ifs), iite;
89+
std::string content(iit, iite);
90+
return content;
91+
}
92+
93+
void file_put_content(std::string const &filename, std::string const &content) {
94+
std::ofstream ofs(filename, std::ios::out | std::ios::binary);
95+
ofs << content;
96+
}
8797
```
8898
89-
## 位域(bit-field)
99+
这样就可以把整个文件读取到内存中,进行处理后再写回文件。
90100
91-
```cpp
92-
TODO
93-
```
101+
> {{ icon.detail }} 用 `std::ios::binary` 选项打开文件,是为了避免文件中出现 `'\n'` 时,被 MSVC 标准库自动转换成 `'\r\n'`,以保证跨平台。
94102
95103
## 别再写构造函数啦!
96104
@@ -1739,8 +1747,42 @@ func(temporary(1));
17391747
17401748
> {{ icon.warn }} 临时变量的生命周期是一行
17411749
1750+
## ostringstream 格式化字符串
1751+
1752+
```cpp
1753+
std::string name = "你好";
1754+
int answer = 42;
1755+
auto str = std::format("你好,{}!答案是 {},十六进制:0x{:02x}\n", name, answer, answer);
1756+
```
1757+
1758+
没有 C++20 之前,要么使用第三方的 `fmt::format`,要么只能使用字符串的 `+` 运算符拙劣地拼接:
1759+
1760+
```cpp
1761+
auto str = std::string("你好,") + name + "!答案是 " + std::to_string(answer) + ",十六进制:0x" + std::to_string(answer) + "\n";
1762+
```
1763+
1764+
这样做效率低下,且不易阅读。而且也无法实现数字按“十六进制”转字符串。
1765+
1766+
可以用 `std::ostringstream`,其用法与 `std::cout` 相同。只不过会把结果写入一个字符串(而不是直接输出),可以用 `.str()` 取出那个字符串。
1767+
1768+
```cpp
1769+
#include <sstream>
1770+
1771+
std::ostringstream oss;
1772+
oss << "你好," << name << "!答案是 " << answer << ",十六进制:0x" << std::hex << std::setfill('0') << std::setw(2) << answer << "\n";
1773+
auto str = oss.str();
1774+
```
1775+
1776+
利用临时变量语法,可以浓缩写在一行里,做个 format 拙劣的模仿者:
1777+
1778+
```cpp
1779+
auto str = (std::ostringstream() << "你好," << name << "!答案是 " << answer << ",十六进制:0x" << std::hex << std::setfill('0') << std::setw(2) << answer << "\n").str();
1780+
```
1781+
17421782
## ADL 机制
17431783

1784+
TODO
1785+
17441786
## shared_from_this
17451787

17461788
## requires 语法检测是否存在指定成员函数
@@ -1749,6 +1791,35 @@ func(temporary(1));
17491791

17501792
## 成员函数针对 this 的移动重载
17511793

1794+
## 位域(bit-field)
1795+
1796+
在互联网编程和各种与硬盘、序列化打交道的场景中,常常需要按位拆分单个字节。
1797+
1798+
C 语言有专门照顾此类工作的语法糖:位域。
1799+
1800+
位域是一种特殊的结构体成员,可以对位进行分组,方便读取。例如,我们想要从一个字节中读取三个状态位:
1801+
1802+
```cpp
1803+
struct Flag {
1804+
uint8_t a : 4; // 低 4 位
1805+
uint8_t b : 4; // 高 4 位
1806+
};
1807+
1808+
sizeof(Flag); // 1 字节大小(共 8 位)
1809+
1810+
Flag f = std::bit_cast<Flag>(0x21);
1811+
f.a; // 0x1
1812+
f.b; // 0x2
1813+
```
1814+
1815+
以上的代码等价于:
1816+
1817+
```cpp
1818+
uint8_t f = 0x21;
1819+
int a = f & 0xF; // 0x1
1820+
int b = f >> 4; // 0x2
1821+
```
1822+
17521823
<!-- ## vector + unordered_map = LRU cache -->
17531824
<!-- -->
17541825
<!-- ## Lambda 捕获 unique_ptr 导致 function 报错怎么办 -->

docs/lambda.md

+20
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,26 @@ int main() {
10281028

10291029
> {{ icon.warn }} 如果不使用 `std::ref`,那么 `main` 里的局部变量 `x` 不会改变!因为 `std::bind` 有一个恼人的设计:默认按拷贝捕获,会把参数拷贝一份,而不是保留引用。
10301030
1031+
有趣的是,placeholder 指定的参数,却不需要 `std::ref` 才能保持引用:
1032+
1033+
```cpp
1034+
int inc(int &x, int y) {
1035+
x += y;
1036+
}
1037+
1038+
int main() {
1039+
int x = 0;
1040+
auto inc1 = std::bind(inc, std::placeholders::_1, 1);
1041+
inc1(x); // 此处 x 是按引用传递的
1042+
fmt::println("x = {}", x); // x = 1
1043+
inc1(x);
1044+
fmt::println("x = {}", x); // x = 2
1045+
return 0;
1046+
}
1047+
```
1048+
1049+
那是因为,`std::placeholders::_1` 指定的参数会被直接完美转发给 `inc` 里的 `x`,相当于 `inc(x, 2);`。只有捕获的参数会发生拷贝,不会完美转发。
1050+
10311051
### bind 是一个失败的设计
10321052
10331053
当我们绑定出来的函数对象还需要接受参数时,就变得尤为复杂:需要使用占位符(placeholder)。

0 commit comments

Comments
 (0)