b
and w
names that I've given the structs!
They just get in the way.
Note: With my compiler, if I remove the b
and w
,
it still compiles, but the fields hSum
etc. do not exist, ie
the compiler simply ignores the whole inner structs.
enum
to define constants, which can be OR'ed together
to form the set of desired flags. However, you cannot use named
enumerations for this because the |
(OR) operator is not
defined on enums, only on ints. Of course unnamed enums have
the disadvantage of having no type safety. Here is an example:
bit_enum
construction would make this sort of situation
so much easier to program, and more type-safe:
Matrix
or Vector
as
an example, but they all use the same type for both operands of a
binary operator, like this:
Matrix<double>
and a Matrix<int>
together?
I would want something like this:
operator+(T1,T2)
returns.
As C++ currently stands, I would
have to explicitly specify the return type in a third template parameter,
which, needless to say, would get ugly pretty quickly.
So I would like to propose the following:
return_type
to find out the
return type of a function. It should work on built-in operators as
well as user-defined ones.
operator+
for MyInt
,
which can take a variety of operands. I can write this as a
template function, which I can then specialize to define
operator+
for new types:
operator+
only if no others match. In C++, every time
you create a subclass of Complex<>
you would
have to redefine operator+
.
This could be done much more elegantly if we could specify restrictions on the template parameters. Here are three possible ways in which restricted template matching could be used to solve the above problem:
T
so that
a type matches T
only if operator+(int,T)
exists.
Perhaps we could use the default template parameter syntax for this:
T2
can be any type, except it is restricted
to being a subclass of Complex<T1>
, for some other
type T1
.
base_type
to represent
the ficticious class from which all types are derived from, including
built-in types. At first sight, the additional base_type
doesn't seem to be putting any restriction on T
at all,
since all types are derived from base_type
. However, this
would change the order of preference for the template matching algorithm,
since MyComplex
would match both Complex<T>
and
base_type
, but Complex<T>
is the better match.
parent
parameter
in List::Iterator::Iterator
to be implicit,
rather like a this
pointer.
The constructor would then invisibly store parent
in
the Iterator
object.
It would go something like this:
auto
, meaning "non-static".
Unfortunately, the current C++ standard treats nested classes as
static by default, so this was necessary to maintain compatibility.
It would have been more logical from a syntax point of view to have
made class
mean non-static, and static class
mean
what we have in the current C++ standard.
Notice that fields from the parent class, such as data
can
be accessed directly. If it is necessary to spell out the names,
you would write List::data
or this->data
or
this->List::data
. This direct access of parent's data
is analogous to ordinary classes, which can access global variables
directly. (Think of static storage as being one big class!!)
Here are some examples of how types could benefit from the same sort of virtual mechanism:
(n=2)
is of type MyInt,
not MyIntExtra, so ExtraTask() is not a method.
This problem must be solved by adding an operator= to the definition of
class MyIntExtra so that it returns a MyIntExtra instead of a MyInt.
This is very cumbersome if you have a lot of subclasses.
But let's say we had a virtual type called type_of_this
,
which, even in the base class, always refers to the type of the top-most
(most-derived) class. Then we could define operator= like this:
Notice that the use of type_of_this
must be severely limited
in order to maintain type-safety. A method whose return type is
type_of_this
cannot return any old MyInt. It must return
a MyInt that is guaranteed to be of type type_of_this
.
Admittedly, that example seems a little esoteric; (who uses the return
value of operator=
anyway?!) But in many cases I think it is
more esthetically pleasing to mimic the
behavior of the standard types as closely as possible.
If there were such a thing as virtual typedef
in C++, we
could add the keyword virtual
at the indicated places,
and the return types and
parameter types of FirstItem
and NextItem
would
change automatically to the correct type.
Again, in the interests of type-safety, it is necessary to note some things:
Item
rather than hiding
the old one, as a regular typedef would do.
virtual
is optional.
type_of_this
is exactly the same as
a virtual typedef
, except that when you derive a subclass,
it is "updated" automatically without you having to type anything.
MyList::NextItem
cannot usually be passed any
old Item* as a parameter. Only one that is guaranteed to be
compatible with the Item* of the most-derived class can be passed.
The rules are complicated for determining whether type-safety is guaranteed and I make no attempt to formulate them here. But to see the subtleties involved, consider this example:
list2
really is
a MyList or if it is a subclass of MyList, whereas list1
definitely is a MyList because we created it right there.
Note: It is tempting to try to extend the virtual
typedef
concept even further. Rather
than defining a struct ListItem
and then creating
a virtual typedef
, it would appear simpler to just declare
the struct as being virtual, like this:
The problem with this is as follows: with virtual typedefs, you can only
create pointers or references, because the size of the type is unknown.
(This is because a derived class may have "updated" it.) But we will,
in all probability, want to create objects of type MyListItem
.
That is why it is better to create a concrete type, and then make a
virtual typedef of it.
But is this a problem? You can also unvirtualize methods by explicit referencing.