I constantly use C++ and regularly discover either by myself or from colleagues or Internet new interesting features of the language with which I wasn't acquainted previously. On the one hand, I'd like to save these for myself, on the other hand, you might find them useful as well. So enjoy the new series. Today's agenda is

  1. Do you need const for a function parameter passed by value?
  2. Iterators that compute their values, overloading the arrow operator.
  3. std::string is not null terminated.
  4. std::cout can be damn fast.

1. Do you need const for a function parameter passed by value?

#include <iostream>

void f(int x) {
    std::cout << "int" << std::endl;
}

void f(const int x) {
    std::cout << "const int" << std::endl;
}

int main() {
    int x = 1;
    const int y = 2;
    f(x);
    f(y);
    return 0;
}

This code failes to compile:

const.cpp: In function ‘void f(int)’:
const.cpp:7:10: error: redefinition of ‘void f(int)’
     void f(const int x) {
          ^
const.cpp:3:10: error: ‘void f(int)’ previously defined here
     void f(int x) {
          ^

So it turns out that you can't overload a function in such a way in contrast to the case int& vs const int&. According to recommendations from Con: Constants and Immutability, if your are pedantic, use const to ensure that compiler checks whether you change this value or not. Or don't be pedantic.

2. Iterators that compute their values, overloading the arrow operator

Say, inspired by enumerate in Python, you want to iterate over a vector, but want to obtain pairs (index, element) instead of just element. You can define your own class with new iterators. But here comes the twist: you would like to have the "arrow operator" for something like it->first. Luckily, the arrow operator doesn't have to return a pointer, in such case the arrow operator will be called on the returned object and so on until there is a pointer returned. That's why it is possible to do something like this:

template <class Pointee>
class FakePointer {
    Pointee data_;
public:
    template <typename... Args>
    explicit FakePointer(Args&&... args)
            : data_(std::forward<Args>(args)...) {
    }

    Pointee* operator->() {
        return &data_;
    }
};

template <typename Value, typename Container>
class IteratorBase : public std::iterator<std::bidirectional_iterator_tag,
                                          Value> {
public:
    // ...
    FakePointer<Value> operator->() const {
        return {index_, container_[index_]};
    }
private:
    size_t index_;
    Container& container_;
};

template <typename T>
class WrappedVector {
public:
    using Iterator = IteratorBase<std::pair<const size_t, T&>,
                                  WrappedVector>;
    using ConstIterator = IteratorBase<const std::pair<const size_t, const T&>,
                                       const WrappedVector>;
    // ...
};

I'd like to note, that like with std::vector<bool> you will not be able to run

std::vector<bool> v = {true, false};
for (auto& e : v) {
    // ...
}

3. std::string is not null terminated

This one is pretty easy and obvious. Yes, you can have '\0' in the middle of your std::string. So don't be afraid to store arbitrary serialized binary data in std::string. It's much better than to have a pointer and data size separately of each other.

4. std::cout can be damn fast

I always thought, that printf beats std::cout under all circumstances. But it turns out, that

cout.sync_with_stdio(false);
cout.imbue(std::locale("C"));

is a total game changer.

comments powered by Disqus