I\'d love to be able to do this:
class myInt : public int
{
};
Why can\'t I?
Why would I want to? Stronger typing. For example, I
You can get what you want with strong typedefs. See BOOST_STRONG_TYPEDEF
Why can't you inherit from int, even though you might want to?
There's no functional reason why you shouldn't be able (in an arbitrary language) inherit from ordinal types such as int, or char, or char* etc. Some languages such as Java and Objective-C actually provide class/object (boxed) versions of the base type, in order to satisfy this need (as well as to deal with some other unpleasant consequences of ordinal types not being objects):
language ordinal type boxed type,
c++ int ?
java int Integer
objective-c int NSNumber
But even Java and objective-c preserve their ordinal types for use... why?
The simple reasons are performance and memory consumption. An ordinal type can be typically be constructed, operated upon, and passed by value in just one or two X86 instructions, and only consumes a few bytes at worst. A class typically cannot - it often uses 2x or more as much memory, and manipulating its value may take many hundreds of cycles.
This means programmers who understand this will typically use the ordinal types to implement performance or memory usage sensitive code, and will demand that language developers support the base types.
It should be noted that quite a few languages do not have ordinal types, in particular the dynamic languages such as perl
, which relies almost entirely on a variadic type, which is something else altogether, and shares some of the overhead of classes.
Why would I want to? Stronger typing. For example, I could define two classes intA and intB, which let me do intA+intA or intB+intB, but not intA+intB.
That makes no sense. You can do all that without inheriting from anything. (And on the other hand, I don't see how you could possibly achieve it using inheritance.) For example,
class SpecialInt {
...
};
SpecialInt operator+ (const SpecialInt& lhs, const SpecialInt& rhs) {
...
}
Fill in the blanks, and you have a type that solves your problem. You can do SpecialInt + SpecialInt
or int + int
, but SpecialInt + int
won't compile, exactly as you wanted.
On the other hand, if we pretended that inheriting from int was legal, and our SpecialInt
derived from int
, then SpecialInt + int
would compile. Inheriting would cause the exact problem you want to avoid. Not inheriting avoids the problem easily.
"Ints don't have any member functions." Well, they have a whole bunch of operators like + and -.
Those aren't member functions though.
Neil's comment is pretty accurate. Bjarne mentioned considering and rejecting this exact possibility1:
The initializer syntax used to be illegal for built-in types. To allow it, I introduced the notion that built-in types have constructors and destructors. For example:
int a(1); // pre-2.1 error, now initializes a to 1
I considered extending this notion to allow derivation from built-in classes and explicit declaration of built-in operators for built-in types. However, I restrained myself.
Allowing derivation from an
int
doesn't actually give a C++ programmer anything significantly new compared to having anint
member. This is primarily becauseint
doesn't have any virtual functions for the derived class to override. More seriously though, the C conversion rules are so chaotic that pretending thatint
,short
, etc., are well-behaved ordinary classes is not going to work. They are either C compatible, or they obey the relatively well-behaved C++ rules for classes, but not both.
As far as the comment the performance justifies not making int a class, it's (at least mostly) false. In Smalltalk all types are classes -- but nearly all implementations of Smalltalk have optimizations so the implementation can be essentially identical to how you'd make a non-class type work. For example, the smallInteger class is represents a 15-bit integer, and the '+' message is hard-coded into the virtual machine, so even though you can derive from smallInteger, it still gives performance similar to a built-in type (though Smalltalk is enough different from C++ that direct performance comparisons are difficult and unlikely to mean much).
The one bit that's "wasted" in the Smalltalk implementation of smallInteger (the reason it only represents 15 bits instead of 16) probably wouldn't be needed in C or C++. Smalltalk is a bit like Java -- when you "define an object" you're really just defining a pointer to an object, and you have to dynamically allocate an object for it to point at. What you manipulate, pass to a function as a parameter, etc., is always just the pointer, not the object itself.
That's not how smallInteger is implemented though -- in its case, they put the integer value directly into what would normally be the pointer. To distinguish between a smallInteger and a pointer, they force all objects to be allocated at even byte boundaries, so the LSB is always clear. A smallInteger always has the LSB set.
Most of this is necessary, however, because Smalltalk is dynamically typed -- it has to be able to deduce the type by looking at the value itself, and smallInteger is basically using that LSB as a type-tag. Given that C++ is statically typed, there's never a need to deduce the type from the value, so you probably wouldn't need to "waste" that bit on a type-tag.
1. In The Design and Evolution of C++, §15.11.3.
In C++ the built-in types are not classes.
If the OP really wants to understand WHY C++ is the way it is, then he should get a hold of a copy of Stroustup's book "The Design and Evolution of C++". It explains the rationale for this and many other design decisions in the early days of C++.