tags:

views:

140

answers:

5

Hi,

I want to declare my own numeric types, exactly like unsigned int, but I do not want the types to be implicitly converted. I tried this first: typedef unsigned int firstID; typedef unsigned int secondID;

but this is no good as the two types are just synonyms for unsigned int, so are freely interchangable.

I'd like this to cause an error:

firstID fid = 0;
secondID sid = fid; // no implicit conversion error

but this to be okay:

firstID fid = 0;
secondID sid = static_cast<secondID>(fid); // no error

My reason is so that function arguments are strongly typed, eg:

void f( firstID, secondID ); // instead of void f(unsigned int, unsigned int)

What is the mechanism I am looking for?

Thanks

Si

+2  A: 

You have to write your own classes for them, reimplementing all the operators you need.

Matteo Italia
Ouch, I expected it to be straightforward! Shame you can't inherit from an unsigned int!
sipickles
You can write the class as a template though, so it will work with all primitive types at least.
OregonGhost
+1  A: 

Ahhh, a fellow Ada traveler I see.

The only real way to do this in C++ is to declare classes. Something like:

class first_100 {
public:
    explicit first_100(int source) {
        if (source < 1 || source > 100) {
           throw range_error;
        }
        value = source;
    };
    // (redefine *all* the int operators here)
private:
    int value;
};

You'll want to make sure to define your int constructor explicit so that C++ won't use it to implicitly convert between your types. That way this will not work:

first_100 centum = first_100(55);
int i = centum;

but something like this might (assuming you define it):

int i = centum.to_int();
T.E.D.
+7  A: 

Maybe BOOST_STRONG_TYPEDEF form boost/strong_typedef.hpp would help.

Alexander Poluektov
Makes sense that Boost would have this. It's not rocket-science, but it is a lot of work to get all the operators, and it's crying out for a template.
T.E.D.
I love boost. Always has an answer
sipickles
+1  A: 
struct FirstID { int id; };
agsamek
+2  A: 

As you noted: typedef is badly named (it should be typealias (D has explicitly added typealias (last time I looked))

So the only way you can do this is to create two unique classes.
I am not going to say you can't write a specialization of static_cast<> to do what you want, but I think (and I have not put that much thought into it yet) doing so would be a bad idea (even if it legal), I think a better approach is to have each class constructor use unsigned int that are explicit (so there is no auto conversion).

 struct FID
 {
     explicit FID(unsigned int v): value(v) {}
     operator unsigned int() {return value;}
     unsigned int value;
 };

 class SID {/* Stuff */};

 FID   fid(1U);
 SID   sid(2U);

 doSomthingWithTwoSID(sid,SID(static_cast<unsigned int>(fid));

Making the constructor explicit means no auto conversion between types.
By adding the built in cast operator to unsigned int means that it can be used anywhere an int is expected.

Martin York