11

I've been programming in C++ for quite a while now, but every now and then I stumble upon a code snippet using STL that would have taken myself quite some time and a lot more code to accomplish.

STL takes quite a while to get used to, and there are not many resources out there with real-life examples on how to use it. Please share your favorite STL feature with me!

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
chris
  • 3,986
  • 1
  • 26
  • 32

9 Answers9

15

Erasing certain elements from a vector in linear time with the erase-remove-idiom:

vec.erase(std::remove(vec.begin(), vec.end(), is_odd), vec.end());

(Manually looping through the vector and erasing on a per-element basis would be quadratic time.)

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
11

dos2unix.cpp

#include <fstream>
#include <iterator>
#include <algorithm>

bool is_cr(char c) { return c == '\r'; }

int main(int, char* a[])
{
    std::ifstream is("/dev/stdin");
    std::ofstream os("/dev/stdout");
    std::istreambuf_iterator<char> in(is), end;
    std::ostreambuf_iterator<char> out(os);
    remove_copy_if(in, end, out, is_cr);
}
chris
  • 3,986
  • 1
  • 26
  • 32
  • 2
    Couldn't you also do: `remove_copy(in, end, out, '\r')` and avoid having to define `is_cr()`? – Fred Larson Nov 18 '10 at 21:55
  • 1
    It's more a demonstration of STL features than the smallest dos2unix. I could also use std::cout/cin without showing how it works using regular files. – chris Nov 19 '10 at 07:12
8

I remember one that I really liked when I stumbled over it (~10 years ago) in comp.lang.c++.moderated:

int main(int argc, char* argv[])
{
  std::vector arguments(argv+1, argv+argc);
  // whatever
}

Today, I don't use this anymore. Why put the stuff into a vector, which you then process by iterators, when you already have iterators to start with? This now doesn't concern so much the STL as a collection of containers and algorithms, but more the idea it brought upon us of gluing sequences and algorithms by iterators:

template<typename It>
int process_arguments(It begin, It end)
{
  // whatever we need to do with those arguments... 
}

int main(int argc, char* argv[])
{
  return process_arguments(argv+1, argv+argc);
}

(Yes, I quite often write small console utilities.)

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 2
    You probably meant `std::vector some_cool_variable_name(argv+1, argv+argc);` right? Because otherwise it is a temporary vector that dies at the semicolon. – fredoverflow Nov 18 '10 at 21:59
7

Using a vector for a buffer. Instead of:

int size_needed = GetData(NULL, 0);
char * buffer = new char[size_needed];
GetData(buffer, size_needed);
...
delete [] buffer;

Using a vector:

int size_needed = GetData(NULL, 0);
std::vector<char> buffer(size_needed);
GetData(&buffer[0], size_needed);
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
4

shared_ptr inside a shared_ptr.

I sometimes use std::shared_ptr custom destructor to implement a simple pooled factory method. Dunno if it counts as a "trick".

class Factory
{
    std::queue<std::shared_ptr<Type>> pool_; // Possibly tbb::concurrent_bounded_queue. Could also be contained as a shared_ptr to allow objects to outlive the factory.
public:
    std::shared_ptr<Type> create()
    {
        std::vector<Type> ptr;
        if(pool.empty())
            ptr = std::make_shared<Type>();
        else
        {
            ptr = std::move(pool_.front());
            pool_.pop();
        }

         return std::shared_ptr<Type>(ptr.get(), [=](Type*){pool_.push(ptr)};); // Put back into pool once destructed
     }
 }
ronag
  • 49,529
  • 25
  • 126
  • 221
4

What I like the most is using the STL to do functional-style coding. For instance, counting the elements smaller than 2:

n = std::count_if(v.begin(), v.end(), std::bind2nd(std::less<int>(), 2));
Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
3

Not particularly useful, but I like faking std::iota (where C++0x is not available) with a call to std::partial_sum:

    std::vector<int> v(5, 1); // 1, 1, 1, 1, 1
    partial_sum(v.begin(), v.end(), v.begin()); // 1, 2, 3, 4, 5

As for something I've actually used in production code: testing if two files are identical:

    if(equal(std::istreambuf_iterator<char>(file1),
             std::istreambuf_iterator<char>(),
             std::istreambuf_iterator<char>(file2)))
    { // files are equal, do whatever

In general, I think partial_sum and inner_product deserve a lot more love than they see. With sufficiently smart functors they can do great things.

Cubbi
  • 46,567
  • 13
  • 103
  • 169
3

My favourite STL trick is avoiding usage of

  • the CRT
  • raw pointers (C++0x).

Expressiveness of STL code versus brute force equivalent is amazing.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
2

Containers, iterators, algorithms: all good stuff. And it's not really a trick, per se, but for me the best thing about the STL is functors. Iterators might be the glue that hold the STL car together, but functors are the engine that makes it really save you time and write better code. Which is, after all, what the STL is all about.

John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Stunning, it is the absolute worst imo. Dreadful locality, nicely fixed by lambdas though. – Hans Passant Nov 19 '10 at 01:51
  • @Hans: When you say "locality" are you referring to having dozens of little functors sprinkled all over the place? – John Dibling Nov 19 '10 at 15:23
  • Yes, eggzactly (tough to misspell on purpose to hit 15 chars). – Hans Passant Nov 19 '10 at 15:35
  • @Hans: I agree it's the big downfall. I'm not trying to convince you that you're wrong (because honestly I dont think you are), but I would say that being smart about the functors you create can help. Instead of 12 purpose-built functors, I like to *try* to build a library of a few general functors I can reuse over & over. This helps. – John Dibling Nov 19 '10 at 16:01
  • @Hans: Here's a philisophical question for you. You say that lambdas are a nice fix. But when you get right down to it, lambdas are really just unnamed functors instantiated at the call point. Aside from thh fact that the code for the lambda is right there as opposed to the code for a functor, is there really any difference? – John Dibling Nov 19 '10 at 16:03
  • Well, the way they can capture local variables is rather nice, also solving the horrid bind function. Herb Sutter had a nice presentation about it: http://player.microsoftpdc.com/Session/ab8fe309-861b-4bc0-b9f1-0398f5a5cba4 – Hans Passant Nov 19 '10 at 16:09
  • @Hans: thx for link. going to watch it – John Dibling Nov 19 '10 at 16:19