Some notes on Chapter 31, 32 (standard library, STL) of Stroustrup’s “The C++ Programming Language, 4th edition”.
Emplace
I’d never heard the word emplace before, but it turns out that it isn’t a word made up for c++, but is also a dictionary word, meaning to “put into place or position”.
c++11 defines some emplace functions. Here’s an example for vector
#include <vector>
#include <iostream>
int main()
{
using pair = std::pair<int, int> ;
using vector = std::vector< pair > ;
vector v ;
pair p{ 1, 2 } ;
v.push_back( p ) ;
v.push_back( {2, 3} ) ;
v.emplace_back( 3, 4 ) ;
for ( auto e : v )
{
std::cout << e.first << ", " << e.second << '\n' ;
}
return 0 ;
}
The emplace_back is like the push_back function, but does not require that a constructed object be created first, either explicitly as in the object p above, or implictly as done with the {2, 3} pair initializer list.
multimap
I’d written some perl code the other day when I wanted a hash that had multiple entries per key. Since my hashed elememts were simple, I just strung them together as comma separated entries (I could have also used a hash of array references). It looks like c++11 builds exactly the construct that I wanted into STL, and has both a multimap and unordered_multimap. Here’s an example of the latter
#include <unordered_map>
#include <string>
#include <iostream>
int main()
{
std::unordered_multimap< int, std::string > m ;
m.emplace( 3, "hi" ) ;
m.emplace( 3, "bye" ) ;
m.emplace( 4, "wow" ) ;
for ( auto & v : m )
{
std::cout << v.first << ": " << v.second << '\n' ;
}
for ( auto f{ m.find(3) } ; f != m.end() ; ++f )
{
std::cout << "find: " << f->first << ": " << f->second << '\n' ;
}
return 0 ;
}
Running this gives me
Observe how nice auto is here. I don’t have to care what the typename for the unordered_multimap find result is. According to gdb that type is:
(gdb) whatis f type = std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<int, std::__1::basic_string<char> >, void*>*> >
Yikes!
STL
The STL chapter outlines lots of different algorithms. One new powerful feature in c++11 is that the Lambdas can be used instead of predicate function objects, which is so much cleaner. I used that capability in a scientific computing programming assignment earlier this year with partial_sort.
The find_if_not algorthim caught my eye, because I just manually coded exactly that sort of loop translating intel assembly that used ‘REPL SCASB’ instructions, and that code was precisely of this find_if_not form. The c++ equivalent of the assembly was roughly of the following form:
int scan3( const std::string & s, char v )
{
auto p = s.begin() ;
for ( ; p != s.end() ; p++ )
{
if ( *p != v )
{
break ;
}
}
if ( p == s.end() )
{
return 0 ;
}
else
{
std::cout << "diff: " << p - s.begin() << '\n' ;
return ( v > *p ) ? 1 : -1 ;
}
Range for can also be used for this loop, but it is only slightly clearer:
int scan2( const std::string & s, char v )
{
auto p = s.begin() ;
for ( auto c : s )
{
if ( c != v )
{
break ;
}
p++ ;
}
if ( p == s.end() )
{
return 0 ;
}
else
{
std::cout << "diff: " << p - s.begin() << '\n' ;
return ( v > *p ) ? 1 : -1 ;
}
}
An STL version of this loop that uses a lambda predicate is
int scan( const std::string & s, char v )
{
auto i = find_if_not( s.begin(),
s.end(),
[ v ]( char c ){ return c == v ; }
) ;
if ( i == s.end() )
{
return 0 ;
}
else
{
std::cout << "diff: " << i - s.begin() << '\n' ;
return ( v > *i ) ? 1 : -1 ;
}
}
I don’t really think that this is any more clear than explicit for loop versions. All give the same results when tried:
int main()
{
std::vector< std::function< int( const std::string &, char ) > > v { scan, scan2, scan3 } ;
for ( auto f : v )
{
int r0 = f( "nnnnn", 'n' ) ;
int rp = f( "nnnnnmmm", 'n' ) ;
int rn = f( "nnnnnpnn", 'n' ) ;
std::cout << r0 << '\n' ;
std::cout << rp << '\n' ;
std::cout << rn << '\n' ;
}
return 0 ;
}
The compiler does almost the same for all three implementations. With the cout’s removed, and compiling with optimization, the respective instruction counts are:
The listings for the STL and the C style for loop are almost the same. The Apple xcode 7 compiler seems to produce slightly more compact code for the range-for version of this function for reasons that are not obvious to me.