83

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 could define two classes intA and intB, which let me do intA + intA or intB + intB, but not intA + intB.

"Ints aren't classes." So what?

"Ints don't have any member data." Yes they do, they have 32 bits, or whatever.

"Ints don't have any member functions." Well, they have a whole bunch of operators like + and -.

Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
Rocketmagnet
  • 5,656
  • 8
  • 36
  • 47
  • 36
    Because the language was not designed that way. –  Jan 26 '10 at 22:16
  • Languages like C# allow you to add "extension methods" to classes (and just about everything, including ints, are classes), typically used to format into strings or what have you. Of course, this doesn't jive with the nearly-bare-metal C++ model. – 3Dave Jan 26 '10 at 22:16
  • 3
    I think Neil's comment nailed it down. It was a design decision to not to make primitive types classes. There's good reasons for doing so and good reasons against. As everything in C++, this, too, is justified by the goal of near-optimal performance. – sbi Jan 26 '10 at 22:29
  • 2
    While I enjoyed Neil's response, it probably didn't do much to educate OP. Reminds me (no offense) of engineering textbooks: if you don't already understand the topic, the explanation is useless. Then again, I did get a good laugh... – 3Dave Jan 26 '10 at 22:32
  • 3
    @OP **"Ints aren't classes" so?** Inheritance, polymorphism and encapsulation are keystones of **object oriented design**. None of these things apply to ordinal types. You can't inherit from an int because it's just a bunch of bytes and has no code. It doesn't have a method table, so there's no way to add methods or override them. – 3Dave Jan 26 '10 at 22:34
  • 13
    As for "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's nonsense. You can implement those types without inheriting from int. Just define a *new* type from scratch, and define its `operator+`. I don't see the problem – jalf Jan 26 '10 at 22:45
  • 7
    If your syntax was legal, then `intA+intB` *would* compile, which was what you wanted to *avoid*. – jalf Jan 26 '10 at 22:51
  • 3
    Downvoted because OP is being belligerent. The question has been answered adequately. @Neil's answer is the best one: the language simply doesn't work that way. The OP should let it go. – Ben Collins Jan 26 '10 at 22:52
  • 6
    @jalf: What if you want all the operations integers support? Surely it isn't much joy to implement them for intA, then the same for intB, etc. etc. – UncleBens Jan 26 '10 at 22:53
  • 2
    But UncleBens, there's the flaw. If you want type-safe operators that *don't* work on all int types but only some, you're going to have to re-implement all the operators anyway. You don't save anything by descending from something that already has the operators. And if you *do* want the operators to work on all int types, then you should just use int. – Rob Kennedy Jan 26 '10 at 22:56
  • 2
    @UncleBens: It may not be much joy, but it's necessary, because the entire premise for the question was **I don't want this type to work in a context where `int` is expected**. It *can't* reuse any int operators because doing so would enable the exact thing that the OP wanted to avoid. – jalf Jan 26 '10 at 23:14
  • 2
    This still doesn't mean that one should accept it, and not wonder if there might be different, yet generic and reusable solutions. – UncleBens Jan 26 '10 at 23:37
  • 3
    Wouldn't inheriting from int for that purpose make degenerate classes that violate LSP? What's wrong with creating a new class with ints and operators as members? – Duncan Jan 27 '10 at 00:15
  • 1
    @UncleBens: Accept what? The OP proposes a feature that, even if it was possible, would not achieve what he actually wanted to do. Before you can blame the language for not supporting his feature, it's worth considering whether the feature would actually solve his problem. In this case, it wouldn't. – jalf Jan 27 '10 at 01:19
  • 4
    No, @David, he doesn't. It took him a while to finally figure out what question he really wanted to ask, but in his second comment to your answer, he finally stated it. (I see that a lot in "why" questions, where a person has trouble expressing that he wants to know the rationale for a design decision rather than the chapter and verse that says something's is or isn't allowed.) I see no evidence that he was rejecting answers to his not-yet-finalized question just to annoy people. – Rob Kennedy Jan 27 '10 at 07:17
  • You should change the title of your question as what you are looking for is not inheriting from int, but having a different type. – Vicente Botet Escriba May 04 '10 at 10:06
  • Why would one one to make such inheritance? `typedef int CostumerId;` `typedef int ProductId;` `Costumer getCostumer(CostumerId cid);` `ProductId pid = 10;` `Costumer c = getCostumer(pid); // mistake allowed!! ` if it was `class CostumerId: int {}; and class ProductId: int {};` it wouldn't be allowed. If I just compose `CostumerId { int id; }` I'll have to overload all operators to get the full functionality, like `(pid++ == ++pid || pid != pid--).` – ribamar Mar 13 '15 at 12:11

19 Answers19

93

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 an int member. This is primarily because int doesn't have any virtual functions for the derived class to override. More seriously though, the C conversion rules are so chaotic that pretending that int, 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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I don't know the internal representation of integers in Smalltalk, but a 15-bit integer is not useful in a language trying to work with or extend C. It's too small to be a `short`, and I doubt it can be used to make compact bitfields. If the cost of being able to derive from raw integer types is one bit of overhead per integer, it's far too high for a language like C++. It's a valid approach in language design in general, but any language that does that will have compatibility issues with C. – David Thornley Jan 27 '10 at 14:43
  • @David Thornley:while it's true that 15 bits is on the small side, keep in mind that smallInteger was defined sometime before 1980. If I were implementing Smalltalk today, I'd probably make it 63 bits, adequate for all integer types except `long long`. – Jerry Coffin Jan 27 '10 at 16:25
  • Do you know if Smalltalk really requires the extra bit? A 63-bit integer is technically legal for `int`, but it's still going to have compatibility problems with C. – David Thornley Jan 28 '10 at 15:58
  • 1
    "Allowing derivation from an int doesn't actually give a C++ programmer anything significantly new compared to having an int member." regardless of the fact this is the creator of the language, it is clearly incorrect, even back then: inheriting from int would inherit all operator semantics, while having int as a member doesn't, so you are forced to add lots and lots of boilerplate – lurscher Nov 02 '21 at 04:14
  • @lurscher: Apparently his definition of "significant" is different from yours. – Jerry Coffin Nov 02 '21 at 05:28
  • This is enough reason to want it: `void f(OneInt); void f(AnotherInt);`. You don't even need two! I'd like `void f(OneInt)` to not accept it if `AnotherInt` is passed to it. I wouldn't even want it to accept a regular int (explicit!). – André Caldas May 31 '23 at 20:43
  • @AndréCaldas: That seems to me more of argument in favor of a "strong typedef" (an alias for an existing type that's still treated as a new, separate type). If you're familiar with Ada, something equivalent to its `Type Foo is new Integer;` – Jerry Coffin Jun 01 '23 at 03:12
  • @JerryCoffin: Yes, I would like a strong typedef. :-) – André Caldas Jun 02 '23 at 09:51
  • 1
    @AndréCaldas: I agree that would be nice. Depending on the situation, you can sometimes get by with `enum class Foo : underlying_int_type` as a semi-reasonable substitute. – Jerry Coffin Jun 02 '23 at 14:35
  • @JerryCoffin: I actually want a double. :-) – André Caldas Jun 03 '23 at 01:01
  • 1
    @AndréCaldas: Yeah, that's a much tougher one (and a large part of why I agree a strong typedef would be a really nice addition). – Jerry Coffin Jun 03 '23 at 03:03
53

Int is an ordinal type, not a class. Why would you want to?

If you need to add functionality to "int", consider building an aggregate class which has an integer field, and methods that expose whatever additional capabilities that you require.

Update

@OP "Ints aren't classes" so?

Inheritance, polymorphism and encapsulation are keystones of object oriented design. None of these things apply to ordinal types. You can't inherit from an int because it's just a bunch of bytes and has no code.

Ints, chars, and other ordinal types do not have method tables, so there's no way to add methods or override them, which is really the heart of inheritance.

3Dave
  • 28,657
  • 18
  • 88
  • 151
  • 1
    If I want *all* the methods, it's becomes a massive and error prone class. – Rocketmagnet Jan 26 '10 at 22:32
  • 2
    @Rocketmagnet, I don't think anyone is arguing the merits of a language allowing you to extend the primitive types (many languages do). However, it is just not possible in C++. The best solution for you, in C++, is to build an aggregate class which has an integer member as suggested by this answer. – Trent Jan 26 '10 at 22:40
  • All **what** methods? Int doesn't have any. – 3Dave Jan 26 '10 at 22:42
  • 3
    All I'd like to know is *why*. Is there some fundamental reason this could not have been done within the design constraints of C++, or did they forget? – Rocketmagnet Jan 26 '10 at 22:45
  • @David: Sorry, I meant operators. – Rocketmagnet Jan 26 '10 at 22:46
  • 12
    Why did someone design the Honda Aztek to look so stupid? It was a design decision. That is the only reason. – snicker Jan 26 '10 at 22:51
  • 2
    @Rocketmagnet - It comes from the C heritage and what, exactly, primitives represent. A primitive in c++ is just a collection of bytes that have little meaning except to the compiler. A class, on the other hand, has a function table, and once you start going down the inheritance and virtual inheritance path, then you have a vtable. None of that is present in a primitive, and by making it present you would a) break a lot of c code that assumes an int is 8 bytes and b) make programs take up a lot more memory. – Mason Jan 26 '10 at 22:52
  • 2
    Mason, making it legal to declare subtypes of int doesn't require adding any storage space to int. It particularly doesn't require adding virtual functions to int. If I have an int subclass that has *added* virtual functions, and I assign it to an ordinary int variable, then I have a slicing problem, but that's not unique to this situation. – Rob Kennedy Jan 26 '10 at 22:59
  • 2
    @Rob If I inherit from int it had better have a virtual destructor. –  Jan 26 '10 at 23:01
  • 1
    @Rob, to work properly with virtual functions int would have to have a vtable. if int didn't have any virtual functions, then there would be no benefit from inheriting from it. – SoapBox Jan 26 '10 at 23:27
  • 3
    @snicker this is quite OT, but the Aztec is by Pontiac, not Honda. – JAL Jan 27 '10 at 00:08
  • 5
    OK, @Soapbox, I'll pose the same question to you as I did to Neil in the comments to Mason's answer. If there's no benefit to inheriting from something without virtual functions, then why was C++ designed to allow that at all? C++ allows allows descending from class types that have no virtual functions, so int's lack of virtual functions cannot be the reason that C++ forbids descending from it. – Rob Kennedy Jan 27 '10 at 00:18
  • 1
    @snicker The Aztek is a Pontiac. If it was a Honda it would have been very boring and sold a LOT, all of which would still be on the road getting great gas mileage, yet cost a little more than you would expect. And they wouldn't be nearly as ugly. – 3Dave Jan 27 '10 at 04:05
  • 2
    About the Honda; that is a bad analogy. Q: Why does the Honda Aztek have 4 wheels? bad A: Because cars have 4 wheels. good A: Three wheels have been tried, and it's unstable. – Rocketmagnet Jan 27 '10 at 22:12
  • @Rob: Other classes do have methods (even if they aren't virtual) which you could benefit from including in a derived class, including some possibly protected ones which then, of course, you could only get by inheriting from it. Also there's obvious benefit to passing around pointers to base classes so that other libraries can use the original base's methods (virtual or not). Since int has no methods at all, these arguments don't apply. You can get basically all of the functionality you want with operator int(). – SoapBox Jan 28 '10 at 00:09
  • "ordinal" types? Perhaps you mean "fundamental" types. Or perhaps the two are just synonyms. I only point it out because there is the type trait std::is_fundamental, and I suspect that evaluates to true for all of the types you refer to. – Trevor Hickey Jan 25 '14 at 17:53
  • 1
    It would be very useful to say that "the class Foo is a float with methods"; the Go programming language provides an existence proof. The canonical example is Fahrenheit and Celsius classes, each of which simply _are_ floats, but which cannot be added to or multiplied by each other. However, you could implement the "AsCelsius" function on a Fahrenheit, and vice versa. – Jonathan Feinberg Oct 04 '14 at 01:54
  • "Method" is **not** a well defined word in C++. Different people have different notion of a "method" is. – curiousguy Aug 18 '15 at 15:25
  • @JonathanFeinberg "_which cannot be added to or multiplied by each other_" so both cannot be "float with methods"! – curiousguy Aug 18 '15 at 15:28
  • > "If you need to add functionality to "int", consider building an aggregate class which has an integer field, and methods that expose whatever additional capabilities that you require." < Then try to use that class with std::atomic and see how you lose half the functionality. – Laurentiu Cristofor Jul 28 '21 at 16:36
  • @LaurentiuCristofor That would be the case for "object" version of an `int` or other primitive. *I* don't see the need for this kind of functionality, but that's not really germane to the question. Guess you could make a `ThreadInt` and wrap everything in a `lock`. :D – 3Dave Jul 29 '21 at 17:45
25

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.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • "Inheriting would cause the exact problem you want to avoid." Only if you were to publicly inherit (as, admittedly, the initial question implied in the code section). Private inheritance would not cause this issue. – addaon Dec 21 '13 at 23:56
  • 1
    @addaon Yes, and private inheritance is a useful idiom when you need to override base class member functions without exposing the base class functionality. What would private inheritance of a concrete type without any virtual functions achieve? – curiousguy Aug 18 '15 at 14:50
  • +1 for the fact that + and - and such aren't member functions. It might be worth mentioning that even for full blown classes many overloaded operators need not be member functions. – YoungJohn Feb 09 '16 at 14:50
16

strong typing of ints (and floats etc) in c++

Scott Meyer (Effective c++ has a very effective and powerful solution to your problem of doing strong typing of base types in c++, and it works like this:

Strong typing is a problem that can be addressed and evaluated at compile time, which means you can use the ordinals (weak typing) for multiple types at run-time in deployed apps, and use a special compile phase to iron out inappropriate combinations of types at compile time.

#ifdef STRONG_TYPE_COMPILE
typedef time Time
typedef distance Distance
typedef velocity Velocity
#else
typedef time float
typedef distance float
typedef velocity float
#endif

You then define your Time, Mass, Distance to be classes with all (and only) the appropriate operators overloaded to the appropriate operations. In pseudo-code:

class Time {
  public: 
  float value;
  Time operator +(Time b) {self.value + b.value;}
  Time operator -(Time b) {self.value - b.value;}
  // don't define Time*Time, Time/Time etc.
  Time operator *(float b) {self.value * b;}
  Time operator /(float b) {self.value / b;}
}

class Distance {
  public:
  float value;
  Distance operator +(Distance b) {self.value + b.value;}
  // also -, but not * or /
  Velocity operator /(Time b) {Velocity( self.value / b.value )}
}

class Velocity {
  public:
  float value;
  // appropriate operators
  Velocity(float a) : value(a) {}
}

Once this is done, your compiler will tell you any places you have violated the rules encoded in the above classes.

I'll let you work out the rest of the details yourself, or buy the book.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
  • Thanks Alex. This is roughly what I'm after. I just wondered if it would be possible to do this without having to re-specify all the operators every time. – Rocketmagnet Jan 27 '10 at 22:07
  • You reference Effective C++. What part do you mean? "Item 18: Make interfaces easy to use correctly and hard to use incorrectly", with its `Date` example? – Frank Kusters Dec 14 '12 at 13:44
  • Once you've implemented the strong type compile, why even leave weak typing as a compile option? – Ponkadoodle Apr 16 '13 at 03:36
  • Performance, code density, icache usage, debuggability, handling platforms with incomplete/incompatible template implementations. – Alex Brown Apr 16 '13 at 18:50
  • 5
    "Performance, code density," : wouldn't a modern compiler completely inline such classes at high optimisation level ? I tried on gcc.godbolt.org and at -O3 it compiles to the exact same ASM instructions: http://pastebin.com/sbqvXxP7 – Jean-Michaël Celerier Jun 27 '15 at 10:47
  • Not if the types are defined in a library, they won't – Alex Brown Jun 28 '15 at 04:56
10

Because int is a native type and not a class

Edit: moving my comments into my answer.

It comes from the C heritage and what, exactly, primitives represent. A primitive in c++ is just a collection of bytes that have little meaning except to the compiler. A class, on the other hand, has a function table, and once you start going down the inheritance and virtual inheritance path, then you have a vtable. None of that is present in a primitive, and by making it present you would a) break a lot of c code that assumes an int is 8 bytes only and b) make programs take up a lot more memory.

Think about it another way. int/float/char don't have any data members or methods. Think of the primitives as quarks - they're the building blocks that you can't subdivide, you use them to make bigger things (apologies if my analogy is a little off, I don't know enough particle physics)

Mason
  • 8,767
  • 10
  • 33
  • 34
  • 2
    That's not an answer, just a restatement of the fact. – Rocketmagnet Jan 26 '10 at 22:31
  • @Rocketmagnet, that does not preclude it from being an answer. Your original question asked why it could not be done. You may not have know it was because `int` is not a class; in some languages `int` *is* a class. – Trent Jan 26 '10 at 22:35
  • 1
    OK, *why* is int not a class ? To me it makes sense to inherit from an int. It would be an object which behaves just like an int. – Rocketmagnet Jan 26 '10 at 22:40
  • 1
    If it would behave just like an `int`, why would you re-invent int? :] – GManNickG Jan 26 '10 at 22:43
  • 4
    It was a design decision when C++ was created that the builtin types would not be classes. Think about it another way. int/float/char don't have any data members or methods. Think of the primitives as quarks - they're the building blocks that you can't subdivide, you use them to make bigger things (apologies if my analogy is a little off, I don't know enough particle physics) – Mason Jan 26 '10 at 22:43
  • @Rocketmagnet, most of us will never know why our 'pet features' of any language are not implemented or not implemented the way we think they *should* be. C++ was designed by committee and this is what they decided on. – Trent Jan 26 '10 at 22:44
  • 1
    @GMan: I think you meant to say "Why would you re-invent int?" – snicker Jan 26 '10 at 22:45
  • 1
    @GMan: So punny. Praise The Dood for comment editing abilities. Also +1 for this answer because it is the correct one. – snicker Jan 26 '10 at 22:49
  • @Mason: It as also a design decision when Java was created, and the Java designers were going heavily into object orientation. They did provide wrapper classes, but somebody could write a wrapper template in C++. – David Thornley Jan 26 '10 at 23:00
  • 1
    I don't see anything about "function tables" being part of a class in the standard, Mason. Classes have member functions, but they don't contribute anything to the size of an object. – Rob Kennedy Jan 26 '10 at 23:04
  • 1
    @Trent The core of C++, still clearly visible today, was not designed by a committee - it was designed (and implemented) principally by one man. Almost all the features in the current C++ standard were present in Cfront 3.0, which well predates it. –  Jan 26 '10 at 23:04
  • @Rob Virtual functions must be implemented somehow, and that implementation must add to the size of an instance. And if you don't have virtual functions, why have inheritance? –  Jan 26 '10 at 23:08
  • 1
    @Neil, if inheritance is pointless without virtual functions, then why would C++ be designed to allow inheritance without virtual functions? – Rob Kennedy Jan 26 '10 at 23:10
  • 1
    @Rob Because it was designed that way. Not every design decision is a good one, but I can't imagine using inheritance without virtual functions - certainly, I've never done so. –  Jan 26 '10 at 23:15
  • @Mason: So we know you're not a string theorist, then. :) – GManNickG Jan 26 '10 at 23:16
  • @Neil, good point about Bjarne not being a committee - I guess we know exactly who to blame then. – Trent Jan 26 '10 at 23:17
  • That's begging the question, @Neil. Every decision has a reason. I can think of a few for why this one *might* have been made. 1. Stroustrup or the committee knew that inheritance without virtuals was useless, but didn't bother formalizing a restriction in the standard — users would figure it out for themselves eventually anyway. 2. There *are* reasons for wanting inheritance without virtuals, so it was decided to allow it. 3. It wasn't a decision at all; the issue simply never occurred to anyone at the time. 4. Someone flipped a coin. – Rob Kennedy Jan 27 '10 at 00:11
  • @Rob: If you read *Design and Evolution*, you will find that Stroustrup made design decisions that looked good at the moment that he later regretted (I think protected inheritance is the best example). However, Stroustrup wanted to leave options open to the programmer, and was loath to forbid anything without good reason. – David Thornley Jan 27 '10 at 14:47
  • That's interesting @David. (I can't actually find protected inheritance mentioned in D&E, I'll believe you if you say it's there.) I'm not sure I understand your point with regard to what I've already written, though. – Rob Kennedy Jan 27 '10 at 15:35
  • @Rob: I'm commenting on your reasons: you seem to have left out "It looked like a good idea to Stroustrup at the moment", as well as commenting on #1: Stroustrup didn't like to make things illegal just because he didn't see a good use. The "protected inheritance" thing may be a fault in my memory, but section 13.9 is about protected data members, and that may be what I was remembering badly. – David Thornley Jan 27 '10 at 16:01
  • A class in C++ does not have a function table, or any other source of overhead. I would expect operations on `class Int { public: int x; };` to compile down to the same code as `int`. – David Stone Nov 16 '17 at 19:16
5

No one has mentioned that C++ was designed to have (mostly) backwards compatibility with C, so as to ease the upgrade path for C coders hence struct defaulting to all members public etc.

Having int as a base class that you could override would fundamentally complicate that rule no end and make the compiler implementation hellish which if you want existing coders and compiler vendors to support your fledgling language was probably not worth the effort.

3Dave
  • 28,657
  • 18
  • 88
  • 151
zebrabox
  • 5,694
  • 1
  • 28
  • 32
  • I don’t see that as valid. The original int wouldn’t behave any different to the int form which you could inherit. And it’s not like C had a method to check whether a type is expected to be a class. – Debilski Jan 27 '10 at 00:46
  • @Debilski BS. If you're using pointer arithmetic to iterate through a malloc'd block of memory, increment by sizeof(int) should be by 2 or 4 bytes ( depending on the bandwidth of your system as defined in limits.h) not by the instance size of an Int, which will certainly be different. Go write a compiler and let us know how it goes. – 3Dave Jan 27 '10 at 03:57
  • Why would the instance size be different? It doesn’t carry any information apart from the value of an `int`? I don’t need my own compiler to prove that. – Debilski Jan 27 '10 at 11:58
  • 1
    @David: From the point of view of language design, an `Int` class that has no ability to have virtual functions is very limited as a base class, and therefore arguably not worth having. From the point of view of implementation, a class with virtual functions needs one vtable pointer per instance. – David Thornley Jan 27 '10 at 14:52
  • 5
    @David: There would be no vtable, because there are no virtual functions. The derived int class would be the same size as the original int. – Rocketmagnet Jan 27 '10 at 21:59
4

What others have said is true... int is a primitive in C++ (much like C#). However, you can achieve what you wanted by just building a class around int:

class MyInt
{
private:
   int mInt;

public:
   explicit MyInt(int in) { mInt = in; }
   // Getters/setters etc
};

You can then inherit from that all you jolly want.

DigitalZebra
  • 39,494
  • 39
  • 114
  • 146
  • 3
    Ok, but you'll want to mark the constructor as `explicit` to prevent automatic conversion. – Steven Sudit Jan 26 '10 at 22:27
  • 2
    But this class behaves nothing like an int. – Rocketmagnet Jan 26 '10 at 22:35
  • You have to go to the trouble of adding all the member operators abd typecasting functions to get it to act like an extended `int` type. But once you do, you can then make it a template type that takes `min` and `max` allowable values, and all of the member functions can do range checking. – David R Tribble Jan 27 '10 at 00:23
  • 2
    Sorry Rocket, you want to "inherit" from int you gotta do the work :) – DigitalZebra Jan 27 '10 at 02:39
  • @Rocketmagnet Not behaving like an `int` is your point, remember: you want stronger type checking. **Allowing any operations like integers do is the opposite of that.** – curiousguy Aug 18 '15 at 15:04
  • What is the preferred method of testing for this? is_arithmetic, is_scalar, is_fundamental, etc. – John P Dec 09 '18 at 02:45
4

As others I saying, can't be done since int is a primitive type.

I understand the motivation, though, if it is for stronger typing. It has even been proposed for C++0x that a special kind of typedef should be enough for that (but this has been rejected?).

Perhaps something could be achieved, if you provided the base wrapper yourself. E.g something like the following, which hopefully uses curiously recurring templates in a legal manner, and requires only deriving a class and providing a suitable constructor:

template <class Child, class T>
class Wrapper
{
    T n;
public:
    Wrapper(T n = T()): n(n) {}
    T& value() { return n; }
    T value() const { return n; }
    Child operator+= (Wrapper other) { return Child(n += other.n); }
    //... many other operators
};

template <class Child, class T>
Child operator+(Wrapper<Child, T> lhv, Wrapper<Child, T> rhv)
{
    return Wrapper<Child, T>(lhv) += rhv;
}

//Make two different kinds of "int"'s

struct IntA : public Wrapper<IntA, int>
{
    IntA(int n = 0): Wrapper<IntA, int>(n) {}
};

struct IntB : public Wrapper<IntB, int>
{
    IntB(int n = 0): Wrapper<IntB, int>(n) {}
};

#include <iostream>

int main()
{
    IntA a1 = 1, a2 = 2, a3;
    IntB b1 = 1, b2 = 2, b3;
    a3 = a1 + a2;
    b3 = b1 + b2;
    //a1 + b1;  //bingo
    //a1 = b1; //bingo
    a1 += a2;

    std::cout << a1.value() << ' ' << b3.value() << '\n';
}

But if you take the advice that you should just define a new type and overload the operators, you might take a look at Boost.Operators

UncleBens
  • 40,819
  • 6
  • 57
  • 90
3

In C++ the built-in types are not classes.

Trent
  • 13,249
  • 4
  • 39
  • 36
3

You can get what you want with strong typedefs. See BOOST_STRONG_TYPEDEF

SCFrench
  • 8,244
  • 2
  • 31
  • 61
Vicente Botet Escriba
  • 4,305
  • 1
  • 25
  • 39
  • This is perfect if - like me - one wants to type check everything and then is OK with a loose typedef. This is not as elegant as @Alex's answer (from Scott Meyers), but cost me one line of code compared to the loose typedef! – sage Mar 16 '17 at 22:27
3

This is a very old topic, but still relevant to many.

Unit-aware programming provides one very important reason why inheriting from intrinsic/fundamental types would be valuable in C++. Numerous well-developed solutions now exist to this problem, but all of them require templates in order to achieve what might otherwise have been handled directly with inheritance, polymorphism, and the strong type-checking of C++. The following is one such alternative for unit-aware programming:

https://benjaminjurke.com/content/articles/2015/compile-time-numerical-unit-dimension-checking/

Inheriting from raw pointers also makes sense (and is also illegal). We can easily create a class where we inherit from a smart pointer thereby extending its behavior, but due to the limitation of C++, we are unable to do the same with a raw pointer. This means that our only way to extend behavior of raw pointers like char* is to write functions that require the pointer to be passed as an argument to the function instead of making them look more like methods you might see within std::string.

Of course, we can always use composition instead of inheritance to get the same effect, but in so-doing, we lose all of the intrinsic operations (such as operator[] and operator++ for a char*) and must remap every one of them that we need to support. Is it doable? Sure. I've done it. Is it easy? Not necessarily. That depends on how much you need to map and how quickly it needs to get done. Is it fast? Depends.

With all of that said, in my view, the biggest argument in favor of inheriting behavior from intrinsic types (including raw pointer types) is that it demonstrates the conceptual consistency of the language. Overall, C++ is very consistent, but it breaks down a bit here in my book as intrinsic types are considered to be special, and frankly, they are not.

2

Well, you don’t really need to inherit anything which hasn’t got any virtual member functions. So even if int were a class, there would not be a plus over composition.

So to say, virtual inheritance is the only real reason you’d need inheritance for anyway; everything else is just saving you masses of typing time. And I don’t think an int class/type with virtual members would be the smartest thing to imagine in the C++ world. At least not for you every day int.

Debilski
  • 66,976
  • 12
  • 110
  • 133
1

What does it mean to inherit from an int?

"int" has no member functions; it has no member data, it's a 32 (or 64) bit representation in memory. It doesn't have it's own vtable. All what it "has" (it doesn't really even own them) are some operators like +-/* that are really more global functions than member functions.

anon
  • 41,035
  • 53
  • 197
  • 293
  • int has a bunch of operators. int has member data consisting of 32 or 64 bits. – Rocketmagnet Jan 26 '10 at 22:36
  • 1
    @Rocketmagnet: The bits of an int are not members of an int. Twisting words does not alter reality. – Chuck Jan 26 '10 at 23:46
  • 4
    What difference does that make, @Chuck? An int holds data. A descendant of an int could hold that data just as easily, no? Why does it need to be called *member* data? – Rob Kennedy Jan 27 '10 at 00:24
  • @Rob Kennedy: What difference does it make? It makes all the difference. Sure, a "descendent" of an int could be represented by the same bit pattern — in fact, that's all it could do, because an int does not have members. A descendent that can't be any different from its parent is no different from a typedef. – Chuck Jan 27 '10 at 01:01
  • 3
    Just because the base type doesn't "have members" doesn't mean a descendant couldn't *add* members. – Rob Kennedy Jan 27 '10 at 01:39
  • Oh, and Int doesn't "have" operators. The fundamental operators that are part of the language grammar support int, and char, and other ordinal types, but these are part of the language, not methods of a class. At some point, operators have to be resolved to atomic operations. Otherwise, the whole language would eat its own tail and the world would implode into a silly paradox. – 3Dave Jan 27 '10 at 04:18
  • 1
    @GMan, Chuck, David: This is all true and right, but it doesn't answer the question "why is it that way". Those were deliberate design decisions. Stating the outcome of these decisions doesn't explain why they were made. – sbi Jan 27 '10 at 06:12
  • @sbi: From my point of view, there's far too many questions about "why is it that way" that seem to assume that a language design emerges full-blown and logically consistent from somewhere, or alternately assume that the language was designed to annoy them by leaving off some favorite feature. There are simply no good answers for many of these questions, aside from "It looked like a good idea to Ritchie/Stroustrup/Gosling/whoever at the time" or "We needed to maintain some compatibility with X". – David Thornley Jan 27 '10 at 14:57
  • @David: I don't see Rocketmagnet rejecting answers because they aren't logical. What I see mostly is people restating the facts ("because they're built-ins!") as an attempt to answer why the facts are the way they are. If you think ints aren't inheritable because Stroustrup thought nobody wanted the feature or because he considered it blatantly stupid to even think of it or whatever - go and answer the question that way. But it is a perfectly legal question that ought to get better answers than the "because!" it mostly got in this thread. – sbi Jan 28 '10 at 15:37
  • 2
    @sbi: And my point is that these questions often have no good answer. A lot of language design decisions are made for no particular reason, and aren't changed later because of compatibility. It happens that Jerry Coffin dug up Stroustrup's reasoning on this case, which is good. Sometimes "because" is the best answer, and it annoys me when people refuse to accept it. – David Thornley Jan 28 '10 at 15:54
  • @David: No, that "because" never is a good answer. It's just an attempt to avoid saying "I don't know" or simply shutting up. What if everybody who didn't know the Why refrained from answering "because"? We'd have a perfectly valid question with the usual three or four answers, at least one of which hit the nail right on the head. Instead we have all this noise where everybody tried to hide the fact that they don't know and earn reputation nevertheless. – sbi Jan 31 '10 at 07:53
  • 2
    @sbi: Sometimes "because" is the only answer. Why does Planck's constant have the value it does? Sometimes the only answer is that it seemed like a good idea at the time. If nobody provided an answer that wasn't certainly good, some questions about language features would go unanswered. Which, I must admit, might be an improvement. – David Thornley Jan 31 '10 at 22:05
  • 1
    There's quite a difference: Planck's constant was _discovered_, while C++' inheritance mechanism was deliberately designed. – sbi Feb 07 '10 at 19:40
  • @sbi "it's like that"/"just because"/"it's the Law" is a valid answer when you know that there is not a better answer, but SO moderators don't like this answer and will remove it without a rational (and no, "not an answer" is not a rational) – curiousguy Aug 18 '15 at 15:17
  • @DavidLively "_Otherwise, the whole language would eat its own tail and the world would implode into a silly paradox_" No it would not. `malloc` is not defined in term of `mmap`. You cannot write mutex in term of std C++. Integers could be part of the std lib. – curiousguy Aug 18 '15 at 15:20
  • @DavidLively: However, there _is_ [a better answer](http://stackoverflow.com/a/2143734/140719). – sbi Aug 23 '15 at 22:45
1

This answer is an implementation of UncleBens answer

put in Primitive.hpp

#pragma once

template<typename T, typename Child>
class Primitive {
protected:
    T value;

public:

    // we must type cast to child to so
    // a += 3 += 5 ... and etc.. work the same way
    // as on primitives
    Child &childRef(){
        return *((Child*)this);
    }

    // you can overload to give a default value if you want
    Primitive(){}
    explicit Primitive(T v):value(v){}

    T get(){
        return value;
    }

    #define OP(op) Child &operator op(Child const &v){\
        value op v.value; \
        return childRef(); \
    }

    // all with equals
    OP(+=)
    OP(-=)
    OP(*=)
    OP(/=)
    OP(<<=)
    OP(>>=)
    OP(|=)
    OP(^=)
    OP(&=)
    OP(%=)

    #undef OP

    #define OP(p) Child operator p(Child const &v){\
        Child other = childRef();\
        other p ## = v;\
        return other;\
    }

    OP(+)
    OP(-)
    OP(*)
    OP(/)
    OP(<<)
    OP(>>)
    OP(|)
    OP(^)
    OP(&)
    OP(%)

    #undef OP


    #define OP(p) bool operator p(Child const &v){\
        return value p v.value;\
    }

    OP(&&)
    OP(||)
    OP(<)
    OP(<=)
    OP(>)
    OP(>=)
    OP(==)
    OP(!=)

    #undef OP

    Child operator +(){return Child(value);}
    Child operator -(){return Child(-value);}
    Child &operator ++(){++value; return childRef();}
    Child operator ++(int){
        Child ret(value);
        ++value;
        return childRef();
    }
    Child operator --(int){
        Child ret(value);
        --value;
        return childRef();
    }

    bool operator!(){return !value;}
    Child operator~(){return Child(~value);}

};

Example:

#include "Primitive.hpp"
#include <iostream>

using namespace std;
class Integer : public Primitive<int, Integer> {
public:
    Integer(){}
    Integer(int a):Primitive<int, Integer>(a) {}

};
int main(){
    Integer a(3);
    Integer b(8);

    a += b;
    cout << a.get() << "\n";
    Integer c;

    c = a + b;
    cout << c.get() << "\n";

    cout << (a > b) << "\n";
    cout << (!b) << " " << (!!b) << "\n";

}
over_optimistic
  • 1,399
  • 2
  • 18
  • 27
  • I wrote my answer before i saw your (we have difference 1.5 hours between our answers). But i'm not sure that your code looks complicated. Could you comment any issue from more simpler code: http://stackoverflow.com/a/26154803/751932 – Maxim Kholyavkin Oct 02 '14 at 05:48
  • @Speakus I don't see your post the link you provided doesn't seem to go to a particular post. I've been coding in c++ so much everything here looks very simple to me. I'll be glad to explain my answer further, just let me know what you don't understand. – over_optimistic Oct 02 '14 at 23:00
  • my answer was deleted: so link i want to show here: https://github.com/Speakus/cppFundamentalClass/blob/master/Primitive.hpp – Maxim Kholyavkin Oct 02 '14 at 23:25
  • In your code you also implement for TT (another type other than T). This removes the strong typeness which is sought after. Also you return a `Primitive` rather than the derived type.Thus again not being useful to the purpose of strong typeness. Try running UncleBens main() function using your class. That should shed light where you need to improve. – over_optimistic Oct 03 '14 at 00:29
  • i really miss strong typing request. Now it's supported in my code too: https://github.com/Speakus/cppFundamentalClass/commit/a3604821606df59725a7849910261a2ff6679155 - tested with UncleBens main() function :) – Maxim Kholyavkin Oct 03 '14 at 01:25
  • Getting better :). using an enum (or number) is not a good idea because someone else can define a `Primitive` of the same number without knowing one already exists. You can do `template class Primitive : public B {...` and pass empty named structs to for `B` – over_optimistic Oct 03 '14 at 02:51
1

Please, excuse me for my poor English.

There is a major difference between C++ correct construction like this:

struct Length { double l; operator =!?:%+-*/...(); };
struct Mass { double l; operator =!?:%+-*/...(); };

and the proposed extension

struct Length : public double ;
struct Mass   : public double ;

And this difference lies on the keyword this behavior. this is a pointer and using a pointer let few chances to use registers for computations, because in usuals processors registers does not have address. Worst, using pointer make the compiler suspicous about the fact that two pointers may designate the same memory.

This will put an extraordinary burden on the compiler to optimize trivials ops.

Another problem is on the number of bugs: reproducing exactly all the behavior of operators is absolutly error prone (for ex making constructor explicit does not forbid all implicits cases). Probability of error while building such an object is quite high. It is not equivalent to have the possibility to do something thru hard work or to have it already done.

Compiler implementors would introduce type checking code (with maybe some errors, but compiler exactness is much better than client code, because of any bug in compiler generate countless errors cases), but main behavior of operation will remain exactly the same, with as few errors than usual.

The proposed alternate solution (using structs during debug phase and real floats when optimized ones) is interesting but has drawbacks: it's raise the probability to have bugs only in optimized version. And debugging optimized application is much costly.

One may implement a good proposal for @Rocketmagnet initial demand for integers types using :

enum class MyIntA : long {}; 
auto operator=!?:%+-*/...(MyIntA);
MyIntA operator "" _A(long);

The bug level will be quite high, like using single member trick, but compiler will treat thoses types exactly like built-in integers (including register capability & optimisation), thanks for inlining.

But this trick can't be used (sadly) for floating numbers, and the nicest need is obviously real valued dimensions checking. One may not mix up apples and pears: adding length and area is a common error.

Stroustrup' invocation by @Jerry is irrelevant. Virtuality is meaningful mainly for public inheritance, and the need is here toward private inheritance. Consideration around 'chaotic' C conversion rules (Does C++14 have anything not chaotic?) of basic type are also not useful : the objective is to have no default conversion rules, not to follow standard ones.

alta
  • 11
  • 2
0

More general than the fact that "int is primitive" is this: int is a scalar type, while classes are aggregate types. A scalar is an atomic value, while an aggregate is something with members. Inheritance (at least as it exists in C++) only makes sense for an aggregate type, because you can't add members or methods to scalars — by definition, they don't have any members.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 1
    for some, `class` is syntax to express a type, and subclassing means restricting the domain. like naturals are a subtype of integers, which are a subtype of reals, etc. – just somebody Jan 27 '10 at 01:04
  • 1
    Guess what? Those "some" are living in la la land. You're talking about how you wish it were, not how it is. I deal in the real world. class is not a generic syntax to express the mathematical concept of type in C++. – Chuck Jan 27 '10 at 06:47
  • What you say is all well and good, except that the question wasn't "how is it?" but "why is it that way?" Neither your answer nor your (somewhat rude) comment answers the right question. – sbi Jan 28 '10 at 15:39
  • The question was "Why can't I do this?" The answer is "Because `int` is a scalar, and the C++ concept of subtyping is not meaningful for scalars." Integers weren't arbitrarily excluded — it's not possible to apply the concept as it exists to them. That is **why**. – Chuck Jan 28 '10 at 18:59
  • No that isn't why. That's just stating the facts without explaining why they are the way they are. There is a very good answer to these questions and, not surprisingly, it also is the accepted answer. – sbi Jan 31 '10 at 07:55
-1

If I remember, this was the main - or one of - the main reasons C++ was not considered a true object oriented language. Java people would say: "In Java, EVERYTHING is an object" ;)

Joe
  • 71
  • 10
  • 11
    Except that Java also has an int that can't be subclassed, so those people would be wrong :-) – paxdiablo Jan 26 '10 at 22:29
  • @Joe: Thankyou, someone who understands the question. – Rocketmagnet Jan 26 '10 at 22:33
  • 2
    Note that people have different definitions of object orientation, so it would be more accurate to say "some people considered C++ not a true O-O language" or specify who. – David Thornley Jan 26 '10 at 23:03
  • 1
    In C++ an `int` is an object. It isn't a `class`. And not all classes have virtual functions, not all classes are designed to serve as base classes. – curiousguy Aug 18 '15 at 15:05
-3

This is related to how the items are stored in memory. An int in C++ is an integral type, as mentioned elsewhere, and is just 32 or 64 bits (a word) in memory. An object, however, is stored differently in memory. It is usually stored on the heap, and it has functionality related to polymorphism.

I don't know how to explain it any better. How would you inherit from the number 4?

Erik A. Brandstadmoen
  • 10,430
  • 2
  • 37
  • 55
  • 7
    Objects are usually stored on the heap in C++? Isn't that a bit of an overgeneralization? – Chuck Jan 26 '10 at 23:44
  • 5
    Also, number 4 isn't a type. It's a value that a variable of type int might hold. Also objects don't necessarily have to be polymorphic (see your STL). – UncleBens Jan 27 '10 at 00:06
  • 6
    Here are two variables: `int x; struct { int i; } y;` Erik, please describe how `x` and `y` are stored differently in memory. – Rob Kennedy Jan 27 '10 at 00:28
  • @UncleBens: There are languages with prototype-based object system, where you can inherit from an instance. This is pretty standard in languages for writing text adventures/interactive fiction, such as Inform and TADS. It's just that C++ doesn't work that way. – David Thornley Jan 27 '10 at 14:59
  • 2
    I don't want to inherit from the number 4, I want to inherit from int. – Rocketmagnet Jan 27 '10 at 22:15
  • @Rob: Structs are also integral (basic) types. They cannot be instantiated, and are not objects. – Erik A. Brandstadmoen Feb 06 '10 at 18:17
  • @Chuck: Where objects are stored, was not the main point of my post. And, you CAN store objects on the heap, if I recall correctly (it's been quite a while since I did C++ programming), But, usually you have a reference to your object (on the stack), and the object itself is stored on the heap. The pointer is destroyed when you exit your scope, however, the object lives on if you don't delete it. This could lead to memory leaks.However, my point was, integral types are not stored in the same way as objects, and it doesn't make sense to inherit from them. Then they would have to be object types – Erik A. Brandstadmoen Feb 06 '10 at 18:22
  • 3
    @Eric, you seem to have no idea what you're talking about. In my previous comment, `y` is an instance of the anonymous struct defined right there. If it had a name (say, `Y`), you could also instantiate it like this: `Y* z = new Y;` The variables `x`, `y`, and now `z`, are all objects. (Check the standard for its definition of *object*; if you think it's "instance of a class," you're wrong.) You *can* store objects on the heap, but saying they're *usually* there is an overstatement. They're frequently on the stack, too. Integral types are stored the same as other types. – Rob Kennedy Feb 06 '10 at 23:34
  • @Rob: I won't get into an argument of C++ details, I'm not proficient enough in the language (far from) to do so. However, I think we are getting a bit off track. The initial question was why the author couldn't inherit from an int (which is a basic type). I'll stop arguing this now, as I feel we're getting a bit OT :) – Erik A. Brandstadmoen Feb 09 '10 at 17:40
-4

Why can't you inherit from int, even though you might want to?

Performance

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.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
  • 2
    You can easily construct wrapper classes in C++ that adds semantics to primitive types but compiles down to the exact same assembly as using the primitive directly, (or calling a non-member function that operates on the primitive). A language need not go the Java way and provide you an Integer class that behaves differently than the primitive equivialent. – nos Jan 27 '10 at 00:08
  • yes, you are right, you can. But they aren't provided out of the box, hence the ? in the table. – Alex Brown Jan 27 '10 at 00:20
  • 1
    also, if there were provided out of the box, they would have virtual functions and be based upon a base class and have members in addition to their main value. count on it. For example, the Java Int type has a lock in it, as well as the int (and who knows what else?) – Alex Brown Jan 27 '10 at 00:21
  • 4
    Alex, I disagree. int has no virtual functions, therefore there will be no vtable. If I add no other members, then MyInt is exactly the same size as int, and should compile to identical code. The only difference will be compile time checking of my types. – Rocketmagnet Jan 27 '10 at 22:22