WebKit 与 C++11

我很高兴地宣布,自 r155146 起,我们现在要求我们的各种端口使用支持某些 C++11 特性的编译器进行构建。这意味着我们不再需要使用 COMPILER_SUPPORTS() 宏来有条件地使用这些特性。这些特性包括:

  • 类型推断
  • 静态断言
  • 移动语义

我们选择了这三个特性,因为它们在我们使用的最新编译器版本中得到了很好的支持:clangMSVCGCC

这对编写代码的人意味着什么?以下是三个示例代码,展示了这三个特性的用武之地:

类型推断

使用 auto 关键字的类型推断将根据变量的初始化器自动推断其类型。这对迭代器尤其有用。此循环

HashMap<OriginStack, OwnPtr<ExecutionCounter> >::const_iterator end = m_counters.end();
for (HashMap<OriginStack, OwnPtr<ExecutionCounter> >::const_iterator iter = m_counters.begin(); iter != end; ++iter) {
    ...
}

变为

for (auto it = m_counters.begin(), end = m_counters.end(); it != end; ++it) {
    ...
}

不幸的是,新的基于范围的 for 循环语法并非所有编译器都支持,但这无疑是朝着正确方向迈出的一步。

静态断言

新的 static_assert 是一种声明编译时断言的方法。如果断言为 false,编译器将产生错误。WTF 已经有一个提供此功能的 COMPILE_ASSERT 宏,但 static_assert 会生成更好的错误消息。

COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);

给出错误

/Source/WTF/wtf/text/AtomicString.cpp:43:1: error: 'dummyatomic_string_and_string_must_be_same_size' declared as an array with a negative size
COMPILE_ASSERT(sizeof(AtomicString) == sizeof(String), atomic_string_and_string_must_be_same_size);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Source/WTF/wtf/text/AtomicString.cpp:23:
In file included from /Source/WTF/config.h:62:
In file included from /Source/WTF/wtf/FastMalloc.h:25:
In file included from /Source/WTF/wtf/PossiblyNull.h:29:
/Source/WTF/wtf/Assertions.h:324:60: note: expanded from macro 'COMPILE_ASSERT'
#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1]

static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String must have the same size");

给出

/Source/WTF/wtf/text/AtomicString.cpp:43:1: error: static_assert failed "AtomicString and String must have the same size"
static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String must have the same size");
^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

移动语义

移动语义可以在按值传递对象时,通过移动数据而不是复制数据来提高性能。对于 WebKit 来说,这意味着我们可以在返回 Vector 的函数中停止使用 out 参数。例如:

void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
{
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
    elements()->namedItems(name, namedItems);

    HTMLElement* elementFromPast = elementFromPastNamesMap(name);
    if (namedItems.size() == 1 && namedItems.first() != elementFromPast)
        addToPastNamesMap(toHTMLElement(namedItems.first().get())->asFormNamedItem(), name);
    else if (elementFromPast && namedItems.isEmpty())
        namedItems.append(elementFromPast);
}

变为

Vector<RefPtr<Node>> HTMLFormElement::namedElements(const AtomicString& name)
{
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#dom-form-nameditem
    Vector<RefPtr<Node>> namedItems = elements()->namedItems(name);

    HTMLElement* elementFromPast = elementFromPastNamesMap(name);
    if (namedItems.size() == 1 && namedItems.first() != elementFromPast)
        addToPastNamesMap(toHTMLElement(namedItems.first().get())->asFormNamedItem(), name);
    else if (elementFromPast && namedItems.isEmpty())
        namedItems.append(elementFromPast);

    return namedItems;
}
注意:由于具名返回值优化,在某些情况下这在过去也可能成立,但现在对于所有内联容量为零的 Vector 对象以及 HashMapHashSet 来说,这样做都是安全的!

移动语义是一个有趣的话题,我希望在另一篇博客文章中进一步探讨,所以这里只做简要提及。

还有一件事

敏锐的读者可能已经注意到上一个例子中我们现在可以使用的另一个 C++11 特性。在 C++11 中,关闭模板参数列表时,右尖括号之间不再需要空格!这意味着

OwnPtr<Vector<RefPtr<Node> > > m_childNodes;

变为

OwnPtr<Vector<RefPtr<Node>>> m_childNodes;

我个人对使用这些特性感到非常兴奋,我认为它们将在整个代码库中发挥作用。未来我们将开始要求支持更多 C++11 特性,但这是一个好的开始。