#include <iostream>
#include <string>
#include <boost/optional.hpp>
using namespace std;
using namespace boost;
optional<string> getline_(istream &st) {
std::string line;
if(getline(st, line)) return line;
return none;
}
int main() {
int lines = 0;
while(getline_(cin)) lines++;
cout << lines << "\n";
}
vs using namespace std;
int main() {
int lines = 0;
string line;
while(getline(cin, line)) lines++;
cout << lines << "\n";
}
Run on an input file of 172,544 lines (the GPL-3 license repeated 256 times), the version using "optional" takes 400ms and performs 1,077,504(!) heap allocations, while the more standard version takes just 280ms and performs just 8 allocations.Of course, "wc -l" (GNU) takes just 9ms, so clearly this isn't a great program either way--but it does serve to show that boost::optional can be a surprising performance sink compared to using a bool return value + out parameter.
(tests on debian jessie amd64, i5-3320M @ 2.6GHz, g++ 4.9 -O3, boost 1.55.0.2)
Something like this (untested) should be much closer to the non-optional version:
#include <iostream>
#include <string>
#include <boost/optional.hpp>
using namespace std;
using namespace boost;
optional<string&> getline_(istream &st, string& line) {
if(getline(st, line)) return line;
return none;
}
int main() {
int lines = 0;
string line;
line.reserve( 1024 ); //1024 should be long enough for any line
while(getline_(cin, line )) lines++;
cout << lines << "\n";
}There are plenty of other cases though where it is useful to use optional (e.g. instead of passing/returning a naked pointer which may or may not be null) and in those cases use of optional will have little/no overhead.
The basic getline interface removes heap pressure by reusing the same string over and over, even though semantically the optional<string> approach may look cleaner. Eric Niebler showed a while ago (I can't remember where) that this API lends itself perfectly to ranges, and can provide both a reasonable interface without sacrificing performance. IIRC, the API looked somewhat like this:
for (auto& line : getline_range(cin))
f(line); // do something with line, e.g., parse.
The semantics are: iterate over standard input until EOF or error and let the range keep (and reuse) the string internally while exposing it as const-reference by dereferencing.The std::unordered_map::insert example in the article is an excellent example of an interface with multiple return values: it returns a pair (iterator, bool) of an iterator to the given key and a bool indicating if the iterator is to a newly inserted key-value pair or if it was the key-value pair already stored in the map. Neither the iterator nor the bool owns a buffer like in the std::getline example, so the std::getline out parameter design is not needed here.