Git Tutorial

git

Lua Tutorial

lua

[译] Item 5: Prefer Auto to Explicit Type Declarations.

看下面的声明,很和谐

1
int x;

等等,擦. 忘记初始化了,所以它的值是不确定的。也许,它可能会被初始化成 0。但是很遗憾这是不确定的。

再看看,解引用一个迭代器来初始化局部变量的例子:

1
2
3
4
5
6
7
8
template <typename It>  // algorithm to dwim ("do what I mean")
void dwim(It b, It e)   // for all elements in range from
{                       // b to e
  while (b != e) {
      typename std::iterator_traits<It>::value_type currValue = *b;
      // ...
  }
}

呃,用 typename std::iterator_traits<It>::value_type 来表示迭代器所指向的值?这样真的没问题嚒?

再来,如果我们想要一个闭包类型的局部变量。哦,好吧。只有编译器才知道这个闭包是什么类型,我们根本无法写出这个类型。

擦,擦,擦。C++ 写起来还真是头疼。没错,不过那都是过去了。有了 C++11 之后,这些问题都不存在了,我们有了 autoauto 类型会根据初始化自动推导,所以它们必须被初始化。这意味着在现代 C++ 中你可以和那些变量未初始化的问题挥手说拜拜了:

1
2
3
int x1;        // potentially uninitialized
auto x2;       // error! initializer required
auto x3 = 0;   // fine, x's valye is well-defined

迭代器解引用初始化局部变量也可以这么来写了:

1
2
3
4
5
6
7
8
template <typename It>  // as before
void dwim(It b, It e)
{
  while (b != e) {
      auto currValue = *b;
      // ...
  }
}
Read on →

[译] Item 4: Know How to View Deduced Types

如何选择查看类型推导结果的工具依赖于你想在开发的那个阶段查看这个类型信息。下面来介绍三种可能的情况,编辑代码时,编译过程中,以及运行时。

IDE 编辑器

当你将鼠标移动到某处代码时,IDE 编辑器通常都会显示出类型(比如,变量,参数,函数的类型等),例如下面的代码:

1
2
3
const int theAnswer = 42;
auto x = theAnswer;
auto y = &theAnswer;

IDE 编辑器应该显示变量 x 的类型是 int,变量 y 的类型是 const int*

要让上面的情况可以工作,你的代码必须是可编译的,因为编辑器之所以能够提示出变量的类型,是因为已经有足够的信息供它内部的编译器(或者至少是编译器前端)来做类型推导的工作。如果信息不足够供编译器做类型推导的话,那么变量类型是不会提示出来的。

对于像 int 一样的简单类型,IDE 生成的信息通常都是正确的。但是对于更复杂的情况,就像后面我们将看到的一样,IDE 生成的信息也许就不是那么有帮助了。

编译器诊断

让编译器提示类型推导的类型的一个有效的办法是,用这个类型构造一个编译失败的错误。编译错误的信息会告诉你这个类型。

考虑之前的例子,我们想看一看 xy 的类型。首先我们声明一个未定义的模板类,它看起来应该是下面这样;

1
2
template <typename T>    // declaration only for TD;
class TD;                // TD == "Type Displayer"

企图实例化这个模板会产生错误,因为这个模板类我们根本就没有定义。接下来,想要看 xy 的类型,我们只需要用它们的类型实例化 TD 就可以了;

1
2
3
TD<decltype(x)> xType;    // elicit errors containing
TD<decltype(x)> yType;    // x's and y's types;
                          // see Item 3 for info on decltype
Read on →

[译] Item 3: Understand Decltype

decltype 是一个奇怪的发明。给一个名字或者表达式,decltype 可以告诉你这个名字或者表达式的类型。通常的情况下,他告诉你的都是你预期的。但是偶尔也会有些出人意料。

我们从最典型的情况开始 —— 那些不会让你惊奇的情况。与模板类型推导和 auto 类型推导不同,decltype 通常只是鹦鹉学舌般的返回你传入的名字或表达式的类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const int i = 0;    // decltype(i) is const int

bool f(const Widget& w);    // decltype(w) is const Widget&
                            // decltype(f) is bool(const Widget&)

struct Point {
  int x, y;               // decltype(Point::x) is int
};                          // decltype(Point::y) is int


Widget w;                   // decltype(w) is Widget

if (f(w)) {                 // decltype(f(w)) is bool
  // ...
}

template<typename T>        // simplified version of std::vector
class vector {
public:
    // ...
    T& operator[](std::size_t index);
    // ...
};

vector<int> v;              // decltype(v) is vector<int>

if (v[0] == 0) {            // decltype(v[0]) is int&
  // ...
}

是不是完全没有惊喜?

Read on →

[译] Item 2: Understand Auto Type Deduction

如果你已经阅读了 Item 1 模板类型推导,那么你应该已经掌握了 auto 的类型推导,出了下面要讲到的一个不同之外,其他都和 Item 1 完全一致,但是你肯定还有疑问,为什么模板类型推导涉及到了模板,函数以及参数,而 auto 却不涉及这些。

没错,但是这也没什么关系。其实模板类型推导与 auto 类型推导有直接的映射关系。有很直观的转换关系。

在 Item 1 中,我们使用下面的模板函数用来描述模板类型推导,

1
2
template <typename T>
void f(ParamType param);

调用如下:

1
f (expr);    // call f with some expression

在调用函数 f 时,编译器使用表达式 expr 来推导类型 TParamType

当使用 auto 来声明变量时,auto 代替了上面 T 的位置,同时变量的类型就是 ParamType 的类型。看下面的例子会更直观,

1
auto x = 27;

在这里,变量 x 的类型标识符就是一个简单的 auto,在

1
const auto cx = x;

中,类型标识符是 const auto,在

1
const auto& rx = x;

中,类型标识符是 const auto&。推导上面 x, cx 以及 rx 的类型,编译器所做的事情就像是有一个模板函数一样,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
void func_for_x(T param);           // conceptual template for deducing x's type

func_for_x(27);                     // conceptual call: param's deduced type is x's type

template <typename T>
void func_for_cx(const T param);    // conceptual template for deducing cx's type

func_for_cx(x);                     // conceptual call: param's deduced type is cx's type

template <typename T>
void func_for_rx(const T& param);   // conceptual template for deducing rx's type

func_for_rx(x);                     // conceptual call: param's deduced type is rx's type
Read on →

[译] Item 1: Understand Template Type Deduction

在使用一个复杂的系统时,我们可以不用知道具体的细节。从这个方面来说,C++ 的模板类型推导是很成功的。数以百万的程序员都使用过模板,即使他们可能很难描述清楚这些类型是如何推导的。

如果你是其中的一员,我有一个好消息,也有一个坏消息。好消息是,模板类型推导是最引人注目的 C++11 新特性 auto 的基础。如果你很清楚 C++98 中的模板类型推导,那么你会很容易明白 C++11 中的 auto。坏消息是,当使用 auto 时,有些类型推导会变得没有那么直观。因此完全掌握类型推导的规则是非常有必要的。Item 1 会介绍你必须知道的类型推导规则。

我们从一小段伪代码开始:

1
2
template <typename T>
void f(ParamType param);

调用如下:

1
f (expr);    // call f with some expression

在编译期间,编译器使用表达式 expr 来推导两个类型:一个是 T 另一个是 ParamType。这两个类型通常是不一样的,因为 ParamType 通常是含有修饰的,比如 const 或者引用。举个例子,如果模板的声明如下:

1
2
template <typename T>
void f (const T& param);    // ParamType is const T&

然后调用如下:

1
2
int x = 0;
f(x);    // call f with an int

类型 T 会被推导为 int,而 ParamType 被推导为 const int&

将类型 T 推导为传入的参数类型是很自然的,即 T 的类型就是 expr 的类型。在上面的例子中,xint 类型,T 被推导为 int 类型。但是有时候却不是这样的。类型推导不仅仅依赖于表达式 expr 的类型,同时还依赖于 ParamType 的形式。有下面三种情况:

  • ParamType 是一个指针或者引用类型,但是不是全局引用。(全局引用将会在 Item 24 中讲述。现在你只需要知道他不同于引用,也不同于右值引用即可)。
  • ParamType 是一个全局引用。
  • ParamType 即不是指针也不是引用
Read on →

《Effecticve Modern C++》预览

虽然最近工作在用 javascript, 但 C++ 也没放下, Scott Meyers 的新书《Effective modern C++》终于在 O’Reilly 上架了. 第一时间买了一本. 2年前的时候, Scott 就给了一版 Effective C++11 的初步想法, 两年后的今天, 顺带着 C++14 终于正式出炉了, 不过目前还是 early release 版本, 还没有经过 review, 正式版要到年底才会放出.

不得不说, C++ 现在终于有了它该有的样子. 大致浏览了一下, 如果现在才开始关注 C++11/C++14 的话, 非常推荐这本书. 如果看过前两代 《Effective C++》和《More effective C++》的话, 那你会再次找到久违的感觉.

全书共分为 6 章, 包括: 类型推导, 新关键字 auto, C++98 到 C++11/14 的变化, 智能指针, 右值引用转移语义与完美转发, lambda 表达式, 并发 API.

关于类型推导, 由于 C++11/14 引入了新的关键字 auto, decltype, 以及新的概念右值引用, 那么类型推导就不仅仅存在于模板中了, 因此从现在开始掌握类型推导的规则是 C++ 的必备技能了.

C++11/14 和以前相比, 变化非常的大, 在这本书中, 用了近 1/3 的章节来讲述如何从 C++98 过度到 C++11/14. 有些甚至推翻了之前的推荐做法, 比如 Item 13: Prefer const_iterators to iterators 就与作者之前的 《Effective STL》中的 item 26 完全相反. 所以对于每个写 C++ 代码的人来说, 这一部分是必须要更新的知识.

对于智能智能, 相信大家早就不陌生, 作者的《More Effective C++》中也讲到了智能指针. 这一次, 终于成为了标准, 本书中也讲到了各种类型智能指针合适的使用场景, 掌握了这些之后, 相信资源管理的问题, 就能够减少很多了.

转移语义很好的解决了性能问题, 完美转发解决了模板函数重载时爆炸式增长的问题, 而右值引用就是这两个特性的基础. 对于那些写库的人来说, 这些特性真是天大的好事.

关于 lambda 表达式, 有了 lambda 表达式, 标准库中的那些算法终于能变得好用了. 在没有 lambda 之前, 在使用每个标准库算法之前, 还需要一个仿函数, 这是一件多么不爽的事情.

对于并发, 在 C++98 以及之前, 标准连多线程的概念都没有, 我们只能使用平台相关的多线程设施, 甚至一些我们觉得没问题的多线程模型都会在这种情况下出问题. (比如: 这篇C++ and the Perils of Double-Checked Locking) 现在标准中不仅有了 thread, 甚至还有了更高级的 task, future, promise. 当然如果能有 Resumable Functions 就更好了, 不过这个特性可能要等到 C++17 了.

Read on →
C++

Javascript New Keyword: Yield

生成器与迭代器

在以前写代码的时候, 涉及到迭代算法时, 通常整个过程中都需要维护一个状态变量, 而我们想使用迭代算法的中间值得时候, 不得不使用回调函数.

下面是一个斐波那契数列的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function do_callback(num) {
  console.log(num);
}
function fib() {
  var i = 0, j = 1, n = 0;
  while (n < 10) {
    do_callback(i);
    var t = i;
    i = j;
    j += t;
    n++;
  }
}
fib();

上面的代码中使用了回调函数, 将小于 10 的斐波那契数列的元素输出到控制台.

迭代器和生成器提供了一个新的, 更好的途径来做同样的事情. 下面是使用生成器实现的代码:

Read on →

首尔-釜山-青岛8日游

五一假期请了 3 天假, 和几个比较好的朋友一起韩国自由行. (原本预谋好的是台湾自由行, 但是由于小伙伴的证件问题, 只能换了路线 ╮(╯▽╰)╭)

出国当然必备护照, 据说有小伙伴办护照填表格瞎填一通, 还被带到了小黑屋.( ⊙ o ⊙ ). 大家要小心千万不要乱填哈. 关于护照可以参考这里.

韩国签证还是比较容易办理, 如果没时间去领事馆的话淘宝花 300 块钱左右就可以代办了, 不需要本人办理, 不需要面签.

酒店在国内提前订好, 回避广告, 就不说在哪里定的了, 行程安排是 首尔-釜山-首尔 各住 2 晚. 人均 200 CNY/天. 釜山便宜一些, 首尔较贵, 但住的都不差. 如果能稍微提高一点点预算的话, 能住的非常好呢.

特别安排了 首尔-釜山-首尔 的路线, 坐了韩国的 KTX 高铁, 对外国游客韩国有 KR PASS, 可以直接在网站上预定. 定了 3 日票, 3 日内可以韩国火车随便乱坐, 虽然我们只坐了 首尔-釜山 的往返, 但也比直接买票划算不少. 关于 KR PASS 还差点搞出乌龙, KR PASS 有普通票和同行票, 同行票需要一个普通票带着才可以, 一张普通票最多可以带 5 个同行票(大概是 9 折左右的优惠), 差点全都买成同行票, 还好在出发前又重新定好. 人均 520 CNY.

作为马斯洛需求体系最底层的 WiFi 需求, 当然要提前做好工作, 由于不通韩语, 英语又没信心 ╮(╯▽╰)╭, 最终决定在国内定 wifi egg(3G路由), 国内还. 40CYN/天. 如果是韩国取韩国还的话, 还能再便宜一些. 但是作为一个极度缺乏安全感的人来说, 还是选择了多花一点点钱, 保证万无一失. ^_^

虽然做了不少准备工作, 也还是有不少 bug, 同行的小伙伴没有一个人用的是 中国电信, 而韩国只有 CMDA2000, 导致大家电话都无法使用, 不过 7-11 就有卖电话卡, 也不是什么大问题. 但是毕竟出门在外, 避免有了信号大家乱跑, 最终还是一致决定所有人都紧紧围绕在 WiFi egg 周围, 保持队形.(^__^) 不分开.

最后就是机票了, 往返机票大约 2400 CNY/人. 国际机票税比较高. 提前一个月定的机票, 如果再提前一些的话可能还会更便宜.

废话结束.

Read on →