Avoiding if statement inside a for loop?

Skamah One picture Skamah One · Jun 1, 2013 · Viewed 12.7k times · Source

I have a class called Writer that has a function writeVector like so:

void Drawer::writeVector(vector<T> vec, bool index=true)
{
    for (unsigned int i = 0; i < vec.size(); i++) {
        if (index) {
            cout << i << "\t";
        }
        cout << vec[i] << "\n";
    }
}

I'm trying not to have a duplicate code, while still worrying about the performance. In the function, I'm doing the if (index) check on every round of my for-loop, even though the result is always the same. This is against "worrying about the performance".

I could easily avoid this by placing the check outside of my for-loop. However, I'll get loads of duplicate code:

void Drawer::writeVector(...)
{
    if (index) {
        for (...) {
            cout << i << "\t" << vec[i] << "\n";
        }
    }
    else {
        for (...) {
            cout << vec[i] << "\n";
        }
    }
}

So these are both "bad" solutions for me. What I've been thinking, is two private functions, one of them outs the index and then calls the other. The other one only outs the value. However, I can't figure out how to use it with my program, I'd still need the if check to see which one to call...

According to the problem, polymorphism seems like a correct solution. But I can't see how should I use it here. What would be the preferred way to solve this kind of problem?

This is not a real program, I'm just interested in learning how this kind of problem should be solved.

Answer

Ali picture Ali · Jun 1, 2013

Pass in the body of the loop as a functor. It gets inlined at compile-time, no performance penalty.

The idea of passing in what varies is ubiquitous in the C++ Standard Library. It is called the strategy pattern.

If you are allowed to use C++11, you can do something like this:

#include <iostream>
#include <set>
#include <vector>

template <typename Container, typename Functor, typename Index = std::size_t>
void for_each_indexed(const Container& c, Functor f, Index index = 0) {

    for (const auto& e : c)
        f(index++, e);
}

int main() {

    using namespace std;

    set<char> s{'b', 'a', 'c'};

    // indices starting at 1 instead of 0
    for_each_indexed(s, [](size_t i, char e) { cout<<i<<'\t'<<e<<'\n'; }, 1u);

    cout << "-----" << endl;

    vector<int> v{77, 88, 99};

    // without index
    for_each_indexed(v, [](size_t , int e) { cout<<e<<'\n'; });
}

This code is not perfect but you get the idea.

In old C++98 it looks like this:

#include <iostream>
#include <vector>
using namespace std;

struct with_index {
  void operator()(ostream& out, vector<int>::size_type i, int e) {
    out << i << '\t' << e << '\n';
  }
};

struct without_index {
  void operator()(ostream& out, vector<int>::size_type i, int e) {
    out << e << '\n';
  }
};


template <typename Func>
void writeVector(const vector<int>& v, Func f) {
  for (vector<int>::size_type i=0; i<v.size(); ++i) {
    f(cout, i, v[i]);
  }
}

int main() {

  vector<int> v;
  v.push_back(77);
  v.push_back(88);
  v.push_back(99);

  writeVector(v, with_index());

  cout << "-----" << endl;

  writeVector(v, without_index());

  return 0;
}

Again, the code is far from perfect but it gives you the idea.