7

Related question: std::map default value for build-in type -- the subtle difference is I want more than to know whether the value is initialized to 0 or garbage, I want to specify a "constructor". I don't even care if it involves overhead with a class definition, I just want a clean "special" basic type. Even a syntactical hack would do. A non basic type is very easy to do this for, it is the entire job of the constructor.

I'd like to have a hashmap unordered_map<void *, int> but to have all its values default-initialized to -1 instead of 0 or garbage. This is because zero is a valid index, and I would prefer to default-initialize with a certainly invalid value.

I think I see a few sloppy ways this might be done with:

struct minus1 {
    int i; 
    minus1() i(-1) {}
};
unordered_map<void*, minus1>

But I don't like this because I have to use .i to access the int, and it really just needs to be an int.

Okay so maybe I can have my map handle this:

struct PointerToIDHash {
    std::unordered_map<void *, int> h;
    PointerToIDHash() {
        // ctor is powerless to affect the initialized values of future insertions into h
    }
};

Well, crap now I have a .h too. Uhhhh. Can I inherit from a template? (sounds scary, but this might be a clean way if it can be pulled off)

How can I make a type that transparently acts like an int but is always initialized to -1?

I would prefer to know both how to do this with and without C++11.

Community
  • 1
  • 1
Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • Did you think about providing a conversion operator? – Zeta Aug 25 '13 at 21:48
  • What's that? :) Tell me more. – Steven Lu Aug 25 '13 at 21:49
  • Have a look at a.lasram's answer. That's basically what I meant. – Zeta Aug 25 '13 at 21:49
  • Sweetness. I knew there was something to do exactly this! – Steven Lu Aug 25 '13 at 21:50
  • Standard library containers typically don't default-initialize their elements, but rather *value*-initialize them. So you never get garbage as long as your element types are sane. – Kerrek SB Aug 25 '13 at 21:59
  • 2
    Don't solve the initialization problem. Instead, solve the problem that you want an `int` to be initialized with an invalid index value. This capability already exists, and it's called `boost::optional`. Then you don't have to use special int values to signal that it's uninitialized, you just check if the optional has been initialized. – Mark B Aug 25 '13 at 23:51
  • *"Can I inherit from a template?"* - Yes, of course you can. *"But can I also inherit from a standard library container (well, why am I even asking this, but just to be sure)?"* - Yes, of course you can (contrary to what others may tell you). So yes, your second solution with a dedicated `PointerToIDHash` would work perfectly, too. – Christian Rau Aug 26 '13 at 08:07
  • @MarkB I'm not sure I prefer putting things into std::pair et. al. unless the data really can't fit in its usual type. I'd rather give up one bit of my integer than expand it to a 64-bit object. That said I'm sure taking such an approach is more maintainable. – Steven Lu Aug 26 '13 at 14:24
  • @Steven Lu Unless you're on embedded hardware, code clarity and future maintainability will be much more important than a few extra bits of space. – Mark B Aug 27 '13 at 12:42

2 Answers2

11
#include <unordered_map>
#include <iostream>

using namespace std;

template<typename T, T default_value>
class SelfInitializer
{
public:
    SelfInitializer(T x = default_value) : x(x) {}
    operator T&() { return x; }
    operator const T&() const { return x; }
private:
    T x;
};

// demo
int main()
{
    using minus1 = SelfInitializer<int, -1>;

    unordered_map<int, minus1> m;

    m[7] = 3; // assignment works

    minus1 x = 3;

    int y = x; // conversion to int works

    int z = int(x); // explicit conversion works

    cout << m[7] << endl;
}
Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
8

add a conversion operator to int& so that your struct minus1 behaves like an int

struct minus1 {
    int i; 
    minus1() : i(-1) {}
    operator int&() { return i; }
};
a.lasram
  • 4,371
  • 1
  • 16
  • 24