Why can't I inherit from int in C ++?

I would really like to do this:

class myInt : public int { }; 

Why can't I?

Why do I need it? Stronger input. For example, I could define two classes intA and intB that allow me to do intA + intA or intB + intB , but not intA + intB .

"Ints are not classes." So what?

"Ints has no member data." Yes, they have 32 bits or something else.

"Ints do not have any member functions." Well, they have a whole group of operators like + and - .

+56
c ++ inheritance integer language-design strong-typing
Jan 26
source share
19 answers

Neal comments quite accurately. Bjarne mentioned, considering and rejecting this exact possibility 1 :

Initializer syntax used to be invalid for built-in types. To allow this, 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 thought of expanding this concept to allow inference from inline classes and explicit declaration of inline operators for inline types. However, I restrained myself.

Enabling inheritance from int does not actually give the C ++ programmer anything new compared to having an int member. This is primarily because int does not have virtual functions to override the derived class. More seriously, however, the C conversion rules are so chaotic that they pretend to be int , short , etc. - these are ordinary classes with good behavior, it won’t work. They are either C compatible or obey C ++ rules regarding good class behavior, but not both.

As for the comment, the performance justifies the fact that it does not make an int class, it is (at least, basically) false. In Smalltalk, all types are classes, but almost all Smalltalk implementations have optimizations, so the implementation can be almost identical to how you would work with non-class types. For example, the smallInteger class represents a 15-bit integer, and the “+” message is hard-coded in the virtual machine, so even if you can get from smallInteger, it still gives a performance similar to the built-in type (although Smalltalk is quite different from C ++ that direct performance comparisons are difficult and are unlikely to mean much).

The one bit that is “lost” in the SmallInteger implementation for Smalltalk (the reason that it represents only 15 bits instead of 16) is probably not needed in C or C ++. Smalltalk is a bit like Java - when you "define an object", you really just define a pointer to the object, and you need to dynamically highlight the object that it will point to. What you manipulate, pass functions as a parameter, etc., is always just a pointer, not the object itself.

This is not how smallInteger is implemented, although - in its case, they put the integer value directly in what will usually be a pointer. To distinguish between smallInteger and pointer, they force all objects to be placed on even byte boundaries, so LSB is always clean. SmallInteger always has LSB installed.

However, most of this is necessary because Smalltalk is dynamically typed - it should be able to determine the type by looking at the value itself, and smallInteger basically uses this LSB as a type tag. Given that C ++ is statically typed, there is no need to infer the type from the value, so you probably won’t need to “spend” this bit on the type tag.




1. In the Design and Development of C ++, §15.11.3.

+81
Jan 27 '10 at 0:22
source share

Int is an ordinal type, not a class. Why do you need this?

If you need to add functionality to "int", consider creating an aggregate class that has an integer field and methods that reveal any additional features that you need.

Update

@OP " Intas aren't classes" right?

Inheritance , polymorphism, and encapsulation are key objects of object-oriented design . None of these things are ordinal types. You cannot inherit from int, because it is just a bunch of bytes and has no code.

Ints, characters, and other ordinal types do not have method tables, so there is no way to add methods or redefine them, which is actually the heart of inheritance.

+52
Jan 26 '10 at 10:10
source share

Why do I need it? Stronger input. For example, I could define two classes intA and intB that allow me to do intA + intA or intB + intB, but not intA + intB.

It does not make sense. You can do all this without inheriting from anything. (And, on the other hand, I don’t see how you could achieve this 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 will not compile exactly the way you wanted.

On the other hand, if we pretended that inheritance from int is legal, and our SpecialInt obtained from int , then SpecialInt + int will compile. Inheritance will lead to the exact problem you want to avoid. Non-inheritance makes it easy to fix the problem.

"Ints do not have any member functions." Well, they have a whole group of operators like + and -.

However, these are not member functions.

+25
Jan 26
source share

If OP really wants to understand WHY C ++ is the way it is, then he should get a copy of Stroustup's book “Design and Evolution of C ++”. This explains the rationale for this and many other design decisions in the early days of C ++.

+14
Jan 26 '10 at 23:15
source share

Since int is a native type, not a class

Edit: translate my comments into my answer.

This comes from the legacy of C and that, in fact, represent the primitives. A primitive in C ++ is just a collection of bytes that make little sense other than a compiler. On the other hand, the class has a table of functions, and as soon as you start to descend the path of inheritance and virtual inheritance, you have a table vtable. None of this is present in the primitive, and by presenting it, you: a) add a lot of c-code, which assumes that int has only 8 bytes, and b) makes programs take up a lot more memory.

Think of it differently. int / float / char do not have any data elements or methods. Think of primitives as quarks - they are building blocks that you cannot separate, you use them to do big things (apologies if my analogy is a little irrelevant, I don’t know enough particle physics)

+10
Jan 26
source share

strong typing of integers (and floating-point numbers) in c ++

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

Strong typing is a problem that can be solved and evaluated at compile time. This means that you can use serial numbers (weak typing) for several types at run time in deployed applications and use a special compilation phase to smooth out unsuitable type combinations. 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 

Then you define your Time , Mass and Distance as classes with all (and only) the corresponding operators, overloaded for the corresponding 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 inform you of any places where you violated the rules encoded in the above classes.

I will let you work through the rest of the details yourself or buy a book.

+9
Jan 27 2018-10-01T00:
source share

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

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

Then you can inherit from everything you need.

+5
Jan 26
source share

No one mentioned that C ++ was designed to have (mostly) backward compatibility with C, in order to facilitate the upgrade path for C encoders, hence the default struct for all participants, etc.

Having an int as a base class that you could override would fundamentally complicate this rule endlessly and make the compiler implementation hellish, which, if you want existing encoders and compiler providers to support your fledged language, is probably not worth the effort .

+4
Jan 27 '10 at 0:35
source share

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

+3
Jan 26
source share

As I said, this is not possible, since int is a primitive type.

I understand the motivation, though, if it's for a stronger typing. For C ++ 0x, it was even suggested that a special typedef of typedef be sufficient for this (but was it rejected?).

Perhaps something can be achieved if you provided the base shell yourself. For example, something like the following, which we hope uses curiously repeating patterns in a legal way and requires only getting 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 can take a look at Boost.Operators

+3
Jan 26 '10 at 23:22
source share

Well, you really don't need to inherit anything that doesn't have any virtual member functions. Therefore, even if int was a class, there would not be a plus over the composition.

So virtual inheritance is the only real reason why you need inheritance anyway; everything else just saves a ton of typing time. And I don't think that an int class / type with virtual members would be the smartest idea to introduce in the C ++ world. At least not for you every day int .

+2
Jan 27
source share

What does it mean to inherit from int?

"int" has no member functions; it has no member data; it is a 32-bit representation in memory. It does not have its own vtable. All he has "(in fact, it's not even their own) are some operators, such as + - / *, which are more global functions than member functions.

+1
Jan 26
source share

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

+1
May 2 '10 at
source share

More general than the fact that "int is primitive": int is a scalar type, and classes are aggregate . A scalar is an atomic value, and an aggregate is something with members. Inheritance (at least since it exists in C ++) makes sense only for the aggregate type, because you cannot add members or methods to scalars - by definition they do not have any members.

0
Jan 26 '10 at 23:40
source share

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"; } 
0
02 Oct '14 at 3:07
source share

Please excuse me for my poor English.

There is a big difference between the correct C ++ construct:

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

and proposed extension

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

And that difference is the behavior of the this . this is a pointer and using a pointer gives you little chance of using registers for calculations, because registers have no address in regular processors. It is worse, using a pointer, to make the compiler suspicious that two pointers may indicate the same memory.

This will put extreme strain on the compiler to optimize trivial operations.

Another problem is the number of errors: reproducing exactly the entire behavior of statements is an absolute error (in order to make the constructor explicit, it does not prohibit all cases of implicits). The probability of error when building such an object is quite high. This is not equivalent to being able to do something through hard work or to do it already done.

Compiler developers will introduce type checking code (maybe some errors, but the accuracy of the compiler is much better than client code, due to some error in the compiler they generate countless errors), but the main behavior of the operation will remain unchanged, as well as a few errors than usually.

The proposed alternative solution (using structures during the debugging phase and real floats during optimization) is interesting, but has its drawbacks: it increases the likelihood of errors only in the optimized version. An optimized debugging application is expensive.

You can implement a good suggestion for an initial @Rocketmagnet request for integer types using:

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

The error level will be quite high, for example, using a one-member trick, but the compiler will treat thoses types in the same way as built-in integers (including registration and optimization), thanks for inlining.

But this trick cannot be used (unfortunately) for floating point numbers, and the most pleasant need is, obviously, checking for valid sizes. Do not mix apples and pears: adding length and area is a common mistake.

Calling Straustrap by @Jerry doesn't matter. Virtuality makes sense mainly for public inheritance, and the need here is for private inheritance. Considering the “chaotic” C conversion rules (does C ++ 14 have something non-chaotic?) The basic type is also not useful: the goal is to not have default conversion rules, and not follow the standard ones.

0
Aug 18 '15 at 14:24
source share

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

-one
Jan 26
source share

This is due to how the elements are stored in memory. Int in C ++ is an integral type, as mentioned elsewhere, and it is only 32 or 64 bits (word) in memory. However, the object is stored differently in memory. It is usually stored in a heap, and it has functionality associated with polymorphism.

I do not know how to explain this better. How would you inherit from number 4?

-3
Jan 26
source share

Why can't you inherit from int even if you want to?

Performance

There are no functional reasons why you should not be able (in an arbitrary language) to inherit from ordinary types such as int or char, or char *, etc. Some languages, such as Java and Objective-C, actually provide a class / object (boxed) of the base type version to satisfy this need (and also deal with some other unpleasant consequences of non-object ordinal types):

 language ordinal type boxed type, c++ int ? java int Integer objective-c int NSNumber 

But even Java and Objective-C retain their ordinal types for use ... why?

Simple reasons are performance and memory consumption. The ordinal type can usually be constructed, operated, and passed by value in only one or two X86 instructions and consumes only a few bytes in the worst case. A class usually cannot - it often uses 2x or more of the same amount of memory, and manipulating its value can take many hundreds of cycles.

This means that programmers who understand this typically use ordinal types to implement performance or memory-sensitive code, and require that language developers support the basic types.

It should be noted that quite a few languages ​​do not have ordinal types, in particular dynamic languages ​​such as perl , which relies almost entirely on the variational type, which is completely different, and shares some of the overhead of classes.

-3
Jan 27 '10 at 0:01
source share



All Articles