mirror of
https://github.com/CnTransGroup/EffectiveModernCppChinese.git
synced 2025-01-27 12:21:01 +08:00
fix typo & format
This commit is contained in:
parent
e974a15c58
commit
b43b6aaae2
@ -3,7 +3,7 @@
|
||||
|
||||
我相信每个人都同意使用STL容器是个好主意,并且我希望Item18能说服你让你觉得使用**std:unique_ptr**也是个好主意,但我猜没有人喜欢写上几次 `std::unique_ptr<std::unordered_map<std::string,std::string>>`这样的类型,它可能会让你患上腕管综合征的风险大大增加。
|
||||
|
||||
避免上述医疗事故也很简单,引入**typedef**即可:
|
||||
避免上述医疗悲剧也很简单,引入**typedef**即可:
|
||||
````cpp
|
||||
typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;
|
||||
````
|
||||
@ -11,7 +11,7 @@ typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapS
|
||||
````cpp
|
||||
using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
|
||||
````
|
||||
这里给出的**typedef**和别名声明做的都是完全一样的事情,我们有理由想知道会不会出于一些技术上的原因两者有一个更好。
|
||||
由于这里给出的**typedef**和别名声明做的都是完全一样的事情,我们有理由想知道会不会出于一些技术上的原因两者有一个更好。
|
||||
|
||||
这里,在说它们之前我想提醒一下很多人都发现当声明一个函数指针时别名声明更容易理解:
|
||||
````cpp
|
||||
@ -43,7 +43,7 @@ struct MyAllocList {
|
||||
};
|
||||
MyAllocList<Widget>::type lw;
|
||||
````
|
||||
它会变得很糟。如果你想使用在一个模板内使用**typedef**声明一个持有链表的对象,而这个对象又使用了模板参数,你就不得不在在**typedef**前面加上**typename**
|
||||
更糟糕的是,如果你想使用在一个模板内使用**typedef**声明一个持有链表的对象,而这个对象又使用了模板参数,你就不得不在在**typedef**前面加上**typename**
|
||||
````cpp
|
||||
template<typename T>
|
||||
class Widget {
|
||||
@ -54,7 +54,7 @@ private:
|
||||
````
|
||||
这里**MyAllocList<T>::type**使用了一个类型,这个类型依赖于模板参数**T**。
|
||||
因此**MyAllocList<T>::type**是一个依赖类型,在C++很多讨人喜欢的规则中的一个提到必须要在依赖类型名前加上**typename**。
|
||||
如果使用别名声明定义一个**MyAllocList**,就不需要使用**typename**(同事省略麻烦的**::type**后缀),
|
||||
如果使用别名声明定义一个**MyAllocList**,就不需要使用**typename**(同时省略麻烦的<b>::type</b>后缀),
|
||||
````cpp
|
||||
template<typename T>
|
||||
using MyAllocList = std::list<T, MyAlloc<T>>; // as before
|
||||
@ -84,24 +84,24 @@ private:
|
||||
… // 一个数据成员!
|
||||
};
|
||||
````
|
||||
就像你看到的,**MyAllocList<Wine>::typ**不是一个类型。
|
||||
如果**Widget**使用**Wine**实例化,在**Widget**模板中的**MyAllocList<Wine>::typ**将会是一个数据成员,不是一个类型。
|
||||
在**Widget**模板内,如果**MyAllocList<Wine>::typ**表示的类型依赖于**T**,编译器就会坚持要求你在前面加上**typename**。
|
||||
就像你看到的,**MyAllocList<Wine>::type**不是一个类型。
|
||||
如果**Widget**使用**Wine**实例化,在**Widget**模板中的**MyAllocList<Wine>::type**将会是一个数据成员,不是一个类型。
|
||||
在**Widget**模板内,如果**MyAllocList<Wine>::type**表示的类型依赖于**T**,编译器就会坚持要求你在前面加上**typename**。
|
||||
|
||||
如果你尝试过模板元编程(TMP), 你一定会碰到取模板类型参数然后基于它创建另一种类型的情况。
|
||||
举个例子,给一个类型**T**,如果你想去掉**T**的常量修饰和引用修饰,比如你想把**const std::string&**变成**const std::string**。
|
||||
又或者你想给一个类型加上**const**或左值引用,比如把**Widget**变成**const Widget**或**Widget&**。
|
||||
(如果你没有用过玩过模板元编程,太遗憾了,因为如果你真的想成为一个高效C++程序员_[1]_,至少你需要熟悉C++的基础。你可以看看我在Item23,27提到的类型转换)。
|
||||
C++11在_type traits_中给了你一系列工具去实现类型转换,如果要使用这些模板请包含头文件<type_traits>。
|
||||
里面不全是类型转换的工具,也包含一些`predictable`接口的工具。给一个类型**T**,你想将它应用于转换中,结果类型就是**std::transformation <T>::type**,比如:
|
||||
C++11在<em>type traits</em>中给了你一系列工具去实现类型转换,如果要使用这些模板请包含头文件<b><type_traits></b>。
|
||||
里面不全是类型转换的工具,也包含一些`predictable`接口的工具。给一个类型**T**,你想将它应用于转换中,结果类型就是**std::transformation\<T\>::type**,比如:
|
||||
````cpp
|
||||
std::remove_const<T>::type // 从const T中产出T
|
||||
std::remove_reference<T>::type // 从T&和T&&中产出T
|
||||
std::add_lvalue_reference<T>::type // 从T中产出T&
|
||||
````
|
||||
注释仅仅简单的中介了类型转换做了什么,所以不要太随便的使用。
|
||||
注释仅仅简单的总结了类型转换做了什么,所以不要太随便的使用。
|
||||
在你的项目使用它们之前,你最好看看它们的详细说明书。
|
||||
尽管写了一些,但我这里不是想给你一个关于type traits使用的教程。注意类型转换尾部的**::type**。
|
||||
尽管写了一些,但我这里不是想给你一个关于type traits使用的教程。注意类型转换尾部的<b>::type</b>。
|
||||
如果你在一个模板内部使用类型参数,你也需要在它们前面加上**typename**。
|
||||
至于为什么要这么做是因为这些type traits是通过在**struct**内嵌套**typedef**来实现的。
|
||||
是的,它们使用类型别名_[2]_技术实现,而正如我之前所说这比别名声明要差。
|
||||
@ -137,7 +137,7 @@ using add_lvalue_reference_t = typename add_lvalue_reference<T>::type;
|
||||
|
||||
记住
|
||||
+ typedef不支持模板化,但是别名声明支持。
|
||||
+ 别名模板避免了使用"::type"后缀,而且在模板中使用typedef还需要在前面加上typename
|
||||
+ 别名模板避免了使用"<b>::type</b>"后缀,而且在模板中使用**typedef**还需要在前面加上**typename**
|
||||
+ C++14提供了C++11所有类型转换的别名声明版本
|
||||
|
||||
## 译注
|
||||
|
Loading…
Reference in New Issue
Block a user