Upd(map): Update a newer version of the type_traits tutorial.
This commit is contained in:
@ -1,18 +1,21 @@
|
|||||||
# A Little Tutorial
|
# A Little Tutorial
|
||||||
|
|
||||||
这个文档对 C++ 11 中的 type_traits 做一个简短的介绍和实现指导。但主要还是需要同学们自学。
|
这个文档对 C++ 11 中的 type_traits 做一个简短的介绍和实现指导。但主要还是需要同学们自学。
|
||||||
## What is traits in C++ 11?
|
|
||||||
|
## What is traits in C++ 11?
|
||||||
|
|
||||||
在 C++11 中,traits 是指一组类型萃取机制(type trait mechanism),用于提取类型信息,并根据这些信息在编译期间进行类型转换和编译期间的决策。它们在头文件 `type_traits` 中。
|
在 C++11 中,traits 是指一组类型萃取机制(type trait mechanism),用于提取类型信息,并根据这些信息在编译期间进行类型转换和编译期间的决策。它们在头文件 `type_traits` 中。
|
||||||
|
|
||||||
traits 通常使用**模板元编程**的技术实现,它允许我们在编译期间确定一个类型的属性,如是否是一个指针、是否是一个常量类型、是否是一个类等等。
|
traits 通常使用**模板元编程**的技术实现,它允许我们在编译期间确定一个类型的属性,如是否是一个指针、是否是一个常量类型、是否是一个类等等。
|
||||||
|
|
||||||
我们给出若干个使用 C++ 11 type_traits 的代码实例:
|
我们给出若干个使用 C++ 11 type_traits 的代码实例:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void print_type(T x)
|
void print_type(const T &x) {
|
||||||
{
|
|
||||||
if (std::is_floating_point<T>::value)
|
if (std::is_floating_point<T>::value)
|
||||||
std::cout << x << " is a floating point number.\n";
|
std::cout << x << " is a floating point number.\n";
|
||||||
else
|
else
|
||||||
@ -20,8 +23,7 @@ void print_type(T x)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
void compare_types(T x, U y)
|
void compare_types(const T &x, const U &y) {
|
||||||
{
|
|
||||||
if (std::is_same<T, U>::value)
|
if (std::is_same<T, U>::value)
|
||||||
std::cout << x << " and "<< y << " have the same type.\n";
|
std::cout << x << " and "<< y << " have the same type.\n";
|
||||||
else
|
else
|
||||||
@ -49,8 +51,10 @@ int main() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
以及traits自定义模板类的实现示例:
|
以及traits自定义模板类的实现示例:
|
||||||
```
|
|
||||||
|
```C++
|
||||||
// 定义数据 type 类
|
// 定义数据 type 类
|
||||||
enum Type {
|
enum Type {
|
||||||
TYPE_1,
|
TYPE_1,
|
||||||
@ -58,43 +62,47 @@ enum Type {
|
|||||||
TYPE_3
|
TYPE_3
|
||||||
}
|
}
|
||||||
// 自定义数据类型
|
// 自定义数据类型
|
||||||
class Foo {
|
struct Foo {
|
||||||
public:
|
static constexpr Type type = TYPE_1;
|
||||||
Type type = TYPE_1;
|
|
||||||
};
|
};
|
||||||
class Bar {
|
struct Bar {
|
||||||
public:
|
static constexpr Type type = TYPE_2;
|
||||||
Type type = TYPE_2;
|
|
||||||
};
|
};
|
||||||
// 定义type traits的模板类,所有像这个模板类一样含有Type的类会被萃取出来进行模板化处理
|
// 定义type traits的模板类,所有像这个模板类一样含有Type的类会被萃取出来进行模板化处理
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct type_traits {
|
struct type_traits {
|
||||||
Type type = T::type;
|
static constexpr Type type = T::type;
|
||||||
}
|
}
|
||||||
// 内置数据类型同样可以进行这种萃取
|
// 内置数据类型同样可以进行这种萃取
|
||||||
template<typename int>
|
template<typename int>
|
||||||
struct type_traits {
|
struct type_traits {
|
||||||
Type type = Type::TYPE_1;
|
static constexpr Type type = Type::TYPE_1;
|
||||||
}
|
}
|
||||||
template<typename double>
|
template<typename double>
|
||||||
struct type_traits {
|
struct type_traits {
|
||||||
Type type = Type::TYPE_3;
|
static constexpr Type type = Type::TYPE_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 萃取之后进行统一处理,写统一的编码函数
|
// 萃取之后进行统一处理,写统一的编码函数
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void decode<const T& data, char* buf) {
|
void decode(const T& data, char* buf) {
|
||||||
if(type_traits<T>::type == Type::TYPE_1) {
|
if (type_traits<T>::type == Type::TYPE_1) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
else if(type_traits<T>::type == Type::TYPE_2) {
|
else if (type_traits<T>::type == Type::TYPE_2) {
|
||||||
...
|
// ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
当然,不仅可以用 `enum + static constexpr`,我们也可以用 `using/typedef` 来定义类型别名,然后通过 `std::is_same` 来判断类型是否相同,来实现类似的功能。
|
||||||
|
|
||||||
## Bonus
|
## Bonus
|
||||||
|
|
||||||
我们的 Bonus 可以是实现类似于 C++ 11 中的 type_traits 的某些功能,但是不要求完全一致。
|
我们的 Bonus 可以是实现类似于 C++ 11 中的 type_traits 的某些功能,但是不要求完全一致。
|
||||||
|
|
||||||
这里举一个示例,我们以“迭代器可否被赋值特性”给出一个简单的实现思路:
|
这里举一个示例,我们以“迭代器可否被赋值特性”给出一个简单的实现思路:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
//1.定义一个 type_traits 模板类
|
//1.定义一个 type_traits 模板类
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -109,16 +117,125 @@ struct my_false_type{
|
|||||||
//todo
|
//todo
|
||||||
};
|
};
|
||||||
//3.分别在可被赋值的迭代器和不可被赋值的迭代器中定义 iterator_assignable 类型
|
//3.分别在可被赋值的迭代器和不可被赋值的迭代器中定义 iterator_assignable 类型
|
||||||
class iterator{
|
struct iterator{
|
||||||
using iterator_assignable = my_true_type;
|
using iterator_assignable = my_true_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class const_iterator{
|
struct const_iterator{
|
||||||
using iterator_assignable = my_false_type;
|
using iterator_assignable = my_false_type;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
这部分我们不会提供提示数据,所以你需要自己设计测试代码,并且在 code review 时向助教展示。
|
|
||||||
|
|
||||||
如果想了解更多关于type traits,可以参考 https://en.cppreference.com/w/cpp/header/type_traits,或许其中的一些做法会给你一些思路的启发。
|
Tips:
|
||||||
|
|
||||||
我们鼓励大家多去尝试,但不建议大家在这个作业中花费太多时间。
|
- 你可以借助 `std::true_type` 和 `std::false_type` 来实现类似功能。
|
||||||
|
- 如果 `true/false` 不能满足你的需求,可以试试看模板类 `std::integral_constant` 。
|
||||||
|
|
||||||
|
### Subtask 1
|
||||||
|
|
||||||
|
在 `C++` 中,`sort` 函数包含在 `algorithm` 头文件中,要求传入的是一对随机访问迭代器。
|
||||||
|
|
||||||
|
如果用户传入了错误的迭代器,会导致编译错误。然而,`gcc` 的默认报错信息实在是难以阅读。同时,对于我们的 `map` 类型,其天然就是按照 `key` 进行排序的。因此在我们的 `sort` 中,我们希望能够特判 `map` 类型迭代器,直接返回;对于随机访问迭代器,我们使用 `std::sort`;而对于其他类型的迭代器,我们希望能够给出更加友好的报错信息。
|
||||||
|
|
||||||
|
> 如果传入了错误的 `list` 迭代器,报错信息可能如下 (没截完, 大约 100+ 行):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
请修改你的 `iterator` 类型,并且完成以下的 `sort` 函数:
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- 为了避免错误的分支被实例化, 你可能需要用到 `if constexpr` 语句
|
||||||
|
- 在 C++ 14 及以后, 可以使用 `is_xxx_v` 代替 `is_xxx<>::value`
|
||||||
|
|
||||||
|
```C++
|
||||||
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
|
||||||
|
template <typename _Iter>
|
||||||
|
void my_sort(_Iter __first, _Iter __last) {
|
||||||
|
// Your code here.
|
||||||
|
// For random access iterator, use std::sort.
|
||||||
|
// For sjtu::map iterator, return directly.
|
||||||
|
// For others, static_assert(false, "Not a random access iterator.");
|
||||||
|
// You may use std::iterator_traits to get the iterator category.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sjtu
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subtask 2
|
||||||
|
|
||||||
|
在 C++ 中,检测一个类是否存在一个成员函数等等是一个非常麻烦的事情。
|
||||||
|
|
||||||
|
在 C++ 20 之前,传统的做法是 [SFINAE](https://en.cppreference.com/w/cpp/language/sfinae) 。然而,这东西写起来非常的麻烦,这里不推荐大家去学习各种抽象的“奇技淫巧”,只需了解大致意图即可。
|
||||||
|
|
||||||
|
幸运的是,在 C++ 20 及以后,我们可以用 `requires` 来轻松的检测,其会返回一个编译期 `bool` 常数。
|
||||||
|
|
||||||
|
```C++
|
||||||
|
template <typename _Tp>
|
||||||
|
static constexpr bool has_value_type = requires() {
|
||||||
|
typename _Tp::value_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
static constexpr bool has_member_dark = requires(const _Tp &x) {
|
||||||
|
x.dark;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
其可用于辅助 `type_traits` 的编写,完成类型检测。
|
||||||
|
|
||||||
|
现在,我们要实现一个递归输出的函数 `print` ,其可以接受任意一个能用 `std::cout` 输出的元素,或者是一个容器 (比如 `std::vector` ) 且容器的元素也能用 `print` 输出。
|
||||||
|
|
||||||
|
对于元素,我们直接输出;对于容器,如果容器里面是元素,我们输出元素,元素间用空格隔开,并且在最后输出一个换行符,前后用 `[]` 包裹;如果容器里面是容器,我们直接先输出单个 `[` 并换行,然后每行递归地输出每个子容器,最后输出一个 `]` 并换行。
|
||||||
|
|
||||||
|
当然,以上要求不是必须的,你可以自由发挥,只要好看即可。
|
||||||
|
|
||||||
|
保证输入合法,且容器有 `begin()` 和 `end()` ,或者是数组。
|
||||||
|
|
||||||
|
Tips:
|
||||||
|
|
||||||
|
- 你可以试试 `std::begin` 和 `std::end` 来获取容器的迭代器。
|
||||||
|
- 感兴趣的同学可以自行去了解一下 `concept`, 可能对你会有帮助。
|
||||||
|
- 如果使用 `requires`, 记得编译选项加上 `-std=c++20`
|
||||||
|
|
||||||
|
```C++
|
||||||
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace sjtu {
|
||||||
|
|
||||||
|
template <typename _Tp>
|
||||||
|
void print(const _Tp &__val) {
|
||||||
|
// Your code here.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sjtu
|
||||||
|
|
||||||
|
|
||||||
|
void sample() {
|
||||||
|
std::vector<int> a[5];
|
||||||
|
for (int i = 0 ; i < 5 ; ++i)
|
||||||
|
a[i].push_back(i), a[i].push_back(i);
|
||||||
|
sjtu::print(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
应当输出类似如下(总之好看即可):
|
||||||
|
[
|
||||||
|
[0 0]
|
||||||
|
[1 1]
|
||||||
|
[2 2]
|
||||||
|
[3 3]
|
||||||
|
[4 4]
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
|
||||||
|
```
|
||||||
|
Reference in New Issue
Block a user