1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
Earlier, in section ref(OUTPUTMODES), it was noted that the
tt([io]fstream::open) members expect an tt(ios::openmode) value as their
final argument. E.g., to open an tt(fstream) object for writing you could do
as follows:
verb(
fstream out;
out.open("/tmp/out", ios::out);
)
Combinations are also possible. To open an tt(fstream) object for
em(both) reading and writing the following stanza is often seen:
verb(
fstream out;
out.open("/tmp/out", ios::in | ios::out);
)
When trying to combine enum values using a `home made' tt(enum) we may run
into problems. Consider the following:
verb(
enum Permission
{
READ = 1 << 0,
WRITE = 1 << 1,
EXECUTE = 1 << 2
};
void setPermission(Permission permission);
int main()
{
setPermission(READ | WRITE);
}
)
When offering this little program to the compiler it replies with an
error message like this:
center(tt(invalid conversion from 'int' to 'Permission'))
The question is of course: why is it OK to combine tt(ios::openmode)
values passing these combined values to the stream's tt(open) member, but
not OK to combine tt(Permission) values.
hi(enum values: and arithmetic operators)
Combining enum values using arithmetic operators results in tt(int)-typed
values. em(Conceptually) this never was our intention. Conceptually it can be
considered correct to combine enum values if the resulting value conceptually
makes sense as a value that is still within the original enumeration
domain. Note that after adding a value tt(READWRITE = READ | WRITE) to the
above tt(enum) we're still not allowed to specify tt(READ | WRITE) as an
argument to tt(setPermission).
To answer the question about combining enumeration values and yet stay
within the enumeration's domain we turn to operator overloading. Up to this
point operator overloading has been applied to class types. Free functions
like tt(operator<<) have been overloaded, and those overloads are conceptually
within the domain of their class.
As bf(C++) is a strongly typed language realize that defining an tt(enum) is
really something beyond the mere association of tt(int)-values with symbolic
names. An enumeration type is really a type of its own, and as with any type
its operators can be overloaded. When writing tt(READ | WRITE) the compiler
performs the default conversion from enum values to tt(int) values and
applies the operator to tt(ints). It does so when it has no alternative.
But it is also possible to overload the enum type's operators. Thus we may
ensure that we'll remain within the enum's domain even though the resulting
value wasn't defined by the enum. The advantage of type-safety and conceptual
clarity is considered to outweigh the somewhat peculiar introduction of values
hitherto not defined by the enum.
Here is an example of such an overloaded operator:
verb(
Permission operator|(Permission left, Permission right)
{
return static_cast<Permission>(static_cast<int>(left) | right);
}
)
Other operators can easily and analogously be constructed.
Operators like the above were defined for the tt(ios::openmode)
enumeration type, allowing us to specify tt(ios::in | ios::out) as argument to
tt(open) while specifying the corresponding parameter as tt(ios::openmode)
as well. Clearly, operator overloading can be used in many situations, not
necessarily only involving class-types.
|