现代C++神器之comma operator

comma operator也就是逗号。常见的用法不过是以下几种:

int a = 1, b = 2, c = 3;
foo(a, b);
tuple<int, float> t{1, 2.f};

这能有什么独特技巧?接着探索:

int a = 1;
int b = 2;
int c = a, d;
int e = (a, b);

第三行已经有点模糊,展开会比较清晰:

int c = a;
int d;

重点来了,第四行会发生什么?

结果是b被赋值给e,编译器在a处报 warning: expression result unused。 这就神奇了。

也就说我们可以将括号当作代码块,其中表达式用逗号隔开,括号的值是最后一个表达式的值。这里确定所有被逗号隔开的表达式都会执行。令人惊奇的是这种操作竟然符合标准。

这时你应该在想那有什么用?接着看:

auto val = (lock_guard(m), v[i]);

咦,上锁竟然如此简单。(上例有潜在性能问题)

进一步,多线程的重入保护也可以作类似简化:

//traditional
void tick(){
 vector<Callback> v;
 {
  lock_guard<mutex> lock(m_mutex);
  swap(v, m_callbacks);
 }
 for (const auto& callback : v) {
  callback();
 }
}
//now
void tick(){
 auto v = (lock_guard<mutex>{m_mutex},
           exchange(m_callbacks, {}));
 for (const auto &callback : v) {
  callback();
 }
}

上例还借助了冷门的std::exchange。(来源

再看个稍复杂的例子,源于Sean Parent 15年一条twitter

template <class F, class... Ts>
void for_each_argument(F f, Ts&&... a) {
 (void)initializer_list<int>{(f(forward<Ts>(a)), 0)...};
}

这个函数这么用:

for_each_argument([](const auto &){//...}, 1, 2.f, 3u);

简单说,就是这个版本的for_each可以遍历处理静态多态的列表。方便起见借助initializer_list展开变长模板,但它只接统一类型的参数。这里逗号操作符就可以派上用场,也就是说(f(forward<Ts>(a)), 0)…会展开成:

(f(1),0) // return-type is int
(f(2.f),0)
(f(3u), 0)
//equal to
f(1)
f(2.f)
f(3u)

看似多此一举,实际达到了目的。

本文最后得出一个结论——高手都玩Twitter 😛

Leave a Reply

Your email address will not be published. Required fields are marked *