[...]
First, I don't think it's unspecified.
This "unspecified" designation is something new to me. I don't really
understand the point of it or how to determine if something is
"implementation defined" or that.
How you determine is by reading the standard. The distinction
is formally important: something that is implementation defined
must be fully defined and specified by the implementation, so if
you are writing for a specific implementation, you can count on
it. The size of an int is a good example: it may vary between
different implementations, but for a given implementation, it
will be the same every time you compile your code (at least with
the same options), and (at least in theory), you can find out
exactly what it is in the documentation. Something which is
unspecified needed be documented, and can vary. The classical
example is order of evaluation: if you write something like 'f()
+ g()', and f() and g() have side effects, the order those side
effects occur is unspecified, and might be different from one
compilation to the next (and often will be different if you
change the optimization options).
There are a few different integral types, that are not boolean, from
which to synthesize "fixed-width" integral types.
'bool' is an integral type. In the rare cases where you need
fixed-width types, you can specify int32_t, and assign a bool to
it, with no problem.
APIs are only half of the story.
So what's the other half?
[...]
Right, they didn't, but had they, they would have stood at
least some chance at not implementing a boolean type that is
broken.
Well, the C++ definition of bool isn't broken. IMHO, the C
definition is, but the C committee obviously doesn't agree with
me on that
. In both cases, the type has been "weakened" by
considerations of backwards compatibility: implicit conversions
in C++ (so that e.g. 'if (p)', where 'p' is a pointer, still
works---the original proposal would have deprecated this, but
the final version didn't), and in C, the need to include a
special header to "activate" bool, and the fact that it is still
a typedef to another integral type.
[...]
Are not 64-bit ints indeed more
efficient (in time) than 32-bit ints, say on x86 vs. x64? Anyone
have a good simple test for this?
It depends on how you use them, but I'd be very surprised if
there were any use cases where 64 bit ints would be faster than
32 bit ints.
I was thinking that the register size and instructions on a 64-bit
machine would somehow favor 64-bit operations.
I can't speak for all 64 bit machines, of course, but at least
on an Intel or a Sparc, there should be no speed difference for
an isolated instance.
In usage, those probably cover enough of the territory. From a language
implementation standpoint, some of those 12 others you have in mind may
be important. What are the 12 others you have in mind?
The most important one is probably the cost of testing one, i.e.
the execution time for 'if ( aBooleanValue )'. In some
contexts, the cost of handling large arrays (where the number of
instances you can fit in a cache line is important) may be
significant.
Note that on most of the systems I currently use, the effective
size of a bool will vary: although the value is effectively
maintained on a single byte, they will pass a bool into a
function as a 4 or 8 byte value, and in a struct, the effective
size (including padding) will depend on what comes after the
bool in the struct, and may be one, two, four or eight bytes.
Do you know of any test suites to analyze such? That is, not the cache
line thing, but the integral type thing, for the latter is the more
abstract thing.
No. In practice, it tends to vary, and even depend on the exact
model of the chip being used. Still, in my current work (which
includes a lot of high performance number crunching), we
consistently find that reducing the size of each element in an
array improves performance accross the board, on all of our
different machines. In one particular case, for example,
replacing:
struct Something { bool isValid; double value; };
std::vector<Something> theData;
with:
std::vector<double> theValues;
std::vector<unsigned char> theIsValidFlags; // 8 flags per entry
resulted in a significant speed-up, despite the additional
effort needed to extract the flag. (Note that I'm not
recommending this in general. It just happened to improve
performance in one particular critical case in our code.)