Skip to content

Commit

Permalink
更新卢瑟日经,形参包展开中的特殊问题
Browse files Browse the repository at this point in the history
  • Loading branch information
Mq-b committed May 27, 2024
1 parent 1d19161 commit 2a866d9
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/卢瑟日经/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
- [如何阅读STL源码?](如何阅读STL源码?.md)
- [godbolt使用文档](godbolt使用文档.md)
- [STL中类的字节大小](STL中类的字节大小.md)
- [形参包展开有6个点?](形参包展开有6个点?.md)
87 changes: 87 additions & 0 deletions src/卢瑟日经/形参包展开有6个点?.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

# C++ 形参包展开有 6 个点?

我们知道 C++ 要想使用可变参数,需要使用到模板中的形参包,而要想得到形参包中的元素,则要进行包展开。例如:

```cpp
template<typename... Args> // 类型形参包
void f(Args...args){ // 函数形参包
// 创造合适的包展开场所(花括号包围的初始化器)
int _[]{ (std::cout << args << ' ',0)... };

std::cout << '\n';

// C++17 折叠表达式展开
((std::cout << args << ' '), ...);
}
```
> [运行](https://godbolt.org/z/MW3KT56fn)测试。
如你所见,这些都是使用了三个点 `...` 进行展开。的确,包展开的语法就是三个点,我之所以会说有 6 个点的情况,那是特殊的,叠加在一起的:
```cpp
template<class Ret, class... Args>
void f(Ret(*func)(Args......)) {
func(1); // 调用函数指针
}
```

> [运行](https://godbolt.org/z/vdseKb3f1)测试。
如你所见,这里的 **`Args......`** 出现了 6 个点,其实不用感到奇怪,记住我们以往教程说的话:

- **C++ 的类型就和拼图一样。**

既然 C++ 形参包展开的语法只有三个点,那么我们就按照已知的去思考。不知道就先不管后面三个点,假设我们传入了 `void``int` 类型,带入进去。`Ret` 就是 `void``Args...` 展开就是 `int`,那么组合起来是?

```cpp
void(*func)(int...)
```

发现了吗?C 语言[变长实参](https://zh.cppreference.com/w/cpp/language/variadic_arguments)罢了,C++11 允许了变长实参的三个点可以不以逗号分隔,不过我们一样也可以分割,也就是原来的函数可以改成:

```cpp
template<class Ret, class... Args>
void f(Ret(*func)(Args...,...));
```
中间加了一个逗号,这也是无所谓的。
---
完整代码:
```cpp
template<class Ret, class... Args>
void f(Ret(*func)(Args......)) { // 加不加逗号都行
func(1); // 调用函数指针
}
// 带可变参数的示例函数
void func(int a, ...) { // 加不加逗号都行
std::cout << "沙贝 C 变长实参函数" << a << std::endl;
}
int main() {
f(func);
}
```

## 总结

如你所见,理所应当非常简单,不懂就先用已知的知识带入就好。

这个问题最初是一个粉丝提出来的,[`std::is_function`](https://zh.cppreference.com/w/cpp/types/is_function) 的文档,提供了这个库的平凡实现(虽然目前没有标准库是这样实现的),里面出现了:

```cpp
// 对常规函数的特化
template<class Ret, class... Args>
struct is_function<Ret(Args...)> : std::true_type {};

// 对变参数函数,如 std::printf 的特化
template<class Ret, class... Args>
struct is_function<Ret(Args......)> : std::true_type {};
```
这样一段。其实注释也说的非常简单直观,不过我们还是要带入理解一下。

0 comments on commit 2a866d9

Please sign in to comment.