[译] 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 →

Web 前端开发小测验, Part 1 之 CSS

这是 @devqinNADbb 上发的一个找虐的测试. (原作者有个提示: Warning: might hurt your feelings).

我来挨个找证据, 今天是 part 1, CSS 部分.

1)

1
2
3
ul {
    MaRGin: 10px;
}

Are CSS property names case-sensitive?

CSS 属性名是大小写敏感的吗 ?

: 不敏感. Cascading Style Sheets, level 17.1 Forward-compatible parsing 最后有这么一段话,

All CSS style sheets are case-insensitive, except for parts that are not under the control of CSS. I.e., in CSS1, font family names and URLs can be case-sensitive. Also, the case-sensitivity of the CLASS and ID attributes is under the control of HTML.

因此, 只有不受 CSS 控制的, 如 font family 的名字, url, 以及受 HTML 控制的 IDclass 大小写敏感, 其他受 CSS 控制的内容都是大小写不敏感的.

2) Does setting margin-top and margin-bottom have an affect on an inline element ?

margin-topmargin-bottom 对内联元素是否有效 ?

: 没效果, Cascading Style Sheets, level 14.2 Inline elements 中有如下描述:

If the inline element has margins, borders, padding or text decorations attached, these will have no effect where the element is broken.

CSS2.1 中, 8.3 Margin properties: ‘margin-top’, ‘margin-right’, ‘margin-bottom’, ‘margin-left’, and ‘margin’ 节关于 margin-topmargin-bottom 有如下描述:

These properties have no effect on non-replaced inline elements.

关于 non-replaced 与 replaced element 的定义可以参考 CSS2.1 中的 Replaced element

3) Does setting padding-top and padding-bottom on an inline element add to its dimensions ?

Read on →
css, web

护照, 往来港澳通行证以及大陆居民往来台湾地区通行证办理记录

首先, 吐槽一下, 在自己国家的土地上都不能自由通行, 还需要办理各种证件. (╯‵□′)╯︵┻━┻

以西安为例, 西安市出入境管理中心在科技路二号(西斜七路十字西南角), 工作时间为法定工作日 9:00-17:00. 据说中午有休息时间. 由于本人 14:50 才到, 中午休息时间未知. (如果一次性需要办多个证件, 需要填多张表格, 所以即使中午休息时间到也没有关系, 领表格自行填写, 填写完之后再办手续).

表格的填写一般都有样例, 不过样例可能已经与现在填写的版本不一致, 这个不用担心, 主要填写基本信息, 不知如何填写的可以留空. 办理时, 工作人员会最后确认的. (这次给我办理的是个制服美女. O(∩_∩)O 哈哈~ 跑题了.

护照

护照是一个国家的公民出入本国国境和到国外旅行或居留时, 由本国发给的一种证明该公民国籍和身份的合法证件. 没有护照是没法出国的. 目前对大陆免签的国家非常少啊, 非常少. (╯‵□′)╯︵┻━┻

护照申请表格一式一份 2 张, 需要本人填写的只有一些基本信息, 办理过程需要录入指纹, 申请表格有一栏是需要工作人员确认信息完成后, 当面签字的. 如果需要邮寄的话, 需要在最后填写邮寄地址. 如果旧护照已过期, 那么与首次申请办理护照没有区别, 旧护照可以自己留作纪念.

往来港澳通行证

内地居民因私往来香港或澳门特别行政区旅游, 探亲, 从事商务, 培训, 就业等非公务活动, 向户口所在地的市, 县公安出入境管理部门提出申请, 凭公安出入境管理部门签发的往来港澳通行证及有效签注才能前往.

如果持有大陆护照, 以及入台的各种证件, 和香港转机机票, 则不需要往来港澳通行证即可在港澳最多停留 7 日.

由于西安目前还不是港澳自由行的城市, 因此还不能办理个人 G 签, 只能办理团队 L 签. 如果只想去香港, 那么只能从深圳找当地旅行团协助过关. 至于有效期及签注次数个人自行选择. 本人选择的是澳门 1 次有效签注, 有效期 1 年. 香港 2 次有效签注, 有效期 1年.

往来港澳通行证申请表格一式一份 1 张, 除了基本信息, 需要注意的是申请的签注类型, 首选个人 G 签, 如果所在城市不支持, 那工作人员会自动帮助你更改为团队 L 签. 如果需要邮寄的话, 需要在最后填写邮寄地址.

Read on →

[译]GotW #95 Solution: Thread Safety and Synchronization

原文地址: GotW #95 Solution: Thread Safety and Synchronization

这篇 GotW 是来回答一些关于线程安全与同步的问题的. 我们的讨论几乎适用于所有主流语言

问题

JG 问题

1) 竞态条件(race condition)指的是什么? 它有很严重吗?

2) 什么是正确同步的程序? 你是如何实现的? 请具体说明.

Guru 问题

3) 考虑下面的代码, some_obj 是一个多个线程可见的共享变量.

1
2
3
4
5
// thread 1 (performs no additional synchronization)
code_that_reads_from( some_obj );  // passes some_obj by const &

// thread 2 (performs no additional synchronization)
code_that_modifies( some_obj );    // passes some_obj by non-const &

如果线程 1 与线程 2 能够并行, 那么当 some_ojb 是如下类型时, 代码是否能够正确同步?

  • a) int
  • b) string
  • c) vector<map<int,string>>
  • d) shared_ptr<widget>
  • e) mutex
  • f) condition_variable
  • g) atomic<unsigned>

提示: 虽然有 7 个类型, 但实际上答案只有两种.

4) 外部同步, 意味着使用共享对象的代码需要自己来保证对象的同步. 回答下面有关外部同步的问题:

  • a) 一般的外部同步的职责是什么?
  • b) 什么是”基本的线程安全保障”?
  • c) 哪些内部同步是在共享变量的实现中需要做的?

5) 完全的内部同步类型(线程安全类型), 意味着所有的同步在对象内部完成, 外部不需要再进行同步. 哪些类型是内部同步的, 为什么?

Read on →

Why Make_shared ?

C++11 中引入了智能指针, 同时还有一个模板函数 std::make_shared 可以返回一个指定类型的 std::shared_ptr, 那与 std::shared_ptr 的构造函数相比它能给我们带来什么好处呢 ?

优点

效率更高

shared_ptr 需要维护引用计数的信息,

  • 强引用, 用来记录当前有多少个存活的 shared_ptrs 正持有该对象. 共享的对象会在最后一个强引用离开的时候销毁( 也可能释放).
  • 弱引用, 用来记录当前有多少个正在观察该对象的 weak_ptrs. 当最后一个弱引用离开的时候, 共享的内部信息控制块会被销毁和释放 (共享的对象也会被释放, 如果还没有释放的话).

如果你通过使用原始的 new 表达式分配对象, 然后传递给 shared_ptr (也就是使用 shared_ptr 的构造函数) 的话, shared_ptr 的实现没有办法选择, 而只能单独的分配控制块:

1
2
auto p = new widget();
shared_ptr sp1{ p }, sp2{ sp1 };

如果选择使用 make_shared 的话, 情况就会变成下面这样:

Read on →
C++

当 Windows API 遇上 RAII

什么是 RAII (Resource Acquisition Is Initialization) ?

RAII (Resource Acquisition Is Initialization), 也称为”资源获取就是初始化”, 是 C++ 语言的一种管理资源, 避免泄漏的惯用法. C++ 标准保证任何情况下, 已构造的对象最终会销毁, 即它的析构函数最终会被调用. 简单的说, RAII 的做法是使用一个对象, 在其构造时获取资源, 在对象生命期控制对资源的访问使之始终保持有效, 最后在对象析构的时候释放资源.

RAII 是保证代码异常安全的重要基础设施. RAII 的使用场景有很多, 如: C++11 中的智能指针, scope lock, scope exit 等等. (早在2000年,Andrei Alexandrescu 就在DDJ杂志上发表了一篇文章,提出了这个叫做 ScopeGuard 的设施)

当 Windows API 遇上 RAII

Windows API 大多是 C 语言风格的函数和句柄, 或者是 COM 风格的接口, 这些用起来都不太方便, 需要进行一定的封装. 至于为什么要封装就不用多说了, 如果你想要异常安全, 想要不必在每个分支中都写清理代码的话, 你一定知道利用 RAII 封装的意义.

ATL 中有对 COM 接口的封装, 智能指针 CComPtr, CComQIPtr 解决了一遍遍的手工 Release 以及 QueryInterface. 但对于普通的 C 语言风格的函数和句柄呢? 难道还要一遍遍的 CloseHandle , ReleaseDC, GlobalUnlock 麽? 弱爆了.

借助 ScopeGuard 和 lambda 表达式(⊙_⊙)? 可以是可以, 但是并不是所有的资源获取都会成功, 那么每次都要产生一个具名的 ScopeGuard, 在申请失败的时候调用 Dismiss, 取消清理的动作嘛? 像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
Acquire Resource1
ScopeGuard release1([&] { /* Release Resource1 */ })
if (!Resource1) {
  release1.Dismiss();
  // throw exception or return or...
}

Acquire Resource2
ScopeGuard release2([&] { /* Release Resource2 */ })
if (!Resource2) {
  release2.Dismiss();
  // throw exception or return or...
}
Read on →
C++