Paul Meng

This is a follow-up of luajit’s benchmark. We use 512 bytes as the key to dedup a big random binary file. I wrote the same benchmark in Rust to get an idea how performant it is. Sadly, with the Rust-1.0 alpha2, its std::old_io runtime library is so terrible in performance. With a naive loop of IO read it would take around 20 seconds on my machine. Even worse then Nodejs’s figures. I then decided to skip the runtime library and implemented it in libc FFI, and get the figures much closer to what was expected, but it is still slower then C++’s naive implementation on Mac OS X.

This is a summary after reading Scott Meyer’s Effective Modern C++. As a programming language with 32 years of history, the new standard of C++11 and C++14 revitalize the whole language. As one of the mainstream PLs, numerous briliant people contribute to the renovation, including celebrity like Bartosz Milewski who set foot both in Haskell and C++. Lots of good design are added (though still with a truck of bad and legacy design tagging along). The new book discuss more practice you should adopt with new standard, and retire the old practices related to C++03. I am going to sum up a few practices and gotcha that I could still recall after finishing the whole book.

move

move is actually a misnomer. It is not moving anything. What it does is actually using a universal reference to capture the variable, and with the correctly deduced T, it using std::remove_reference_t to remove the statically cast the lvalue to rvalue. the naming of it doesn’t involve any movement. It is the rvalue gives compilers the hint about what could be optimized. And move doesn’t necessarily mean it is cheaper than copy. You still have to check out if the implemntation for rvalue operation to see how much does it cost.

noexcept

The containers in the standard library doesn’t all adopt the move semantic of any user defined type automatically with you set the compiler flag. Most of them use a conditaionl move strategy to avoid the problem of exceeption. Therefore, in order to take the benefit from move semantic. You have to somehow indicate that your function would not throw any exception. Which is noexcept keyword. But this keyword could be used as the operator to tell if the function would throw exception or not. The following is a snippet that implement a swap only does its job when the individual elemental swap doesn’t throw any exception.

perfect forwarding

Perfect forwarding in 99% of the scenarios would be perfectly functional. Some exceptions that it doesn’t work is that the constructor has different meaning with the argument list (a, b) and initializer list {a, b}. std::vector is one of them. std::vector a(2, 3) means create a vector with two 3. but not a vector with its content [2, 3]

emplace_back

emplace_back is a good way to save the operation cost, but it is not a explixir and could results to different meaning. Like std::regex. push_back a null_ptr would make compiler whining, but compiler would happily accept null_ptr if you call emplace_back, and blows up at runtime.

Epilogue

It’s amazing that C++ could still have great advancement in recent years, with all of its love and hate. The cost is that the language has been so complicated that few people could fully grasp. It’s also an interesting observation where that most of the interviews you met in an intervew who proclaim that they are well-versed C++ programmer are more than fifty percent likely that they are not. However, those who expresses their lack of confidence in knowing the language actually the group who are more competitive.