Rationale for two C++ features

S

sean_in_raleigh

Hi,

I'm curious what the rationale was for two
oddities in C++.

1) Why did the standards committee choose
the oddball no-extension names for the standard
headers (<string>, e.g.)? I can understand them wanting to
emphasize that an included entity is not necessarily
a file, but they say "header file" throughout the standard,
and calling it something like "string.hpp" would not have implied
that it was necessarily a file.


2) Why was the following use of the conditional operator
made illegal?

class B { };
class D1 : public B { };
class D2 : public B { };
int main(int argc, char **argv) {
B *p1 = argc == 1 ? new D1() : new B();
B *p2 = argc == 1 ? new D1() : new D2();
}

In the code above, the p1 assignment is legal,
while the p2 assignment is not (according to
the standard's expr.cond.5, and the Comeau
and GNU compilers I tried). Why not just
treat it as the following:

B *p2;
if (argc == 1)
p2 = new D1();
else
p2 = new D2();

Thanks,
Sean
 
P

Pete Becker

they say "header file" throughout the standard,

I only found two places in the standard where "header file" occurs, and
both are in the C compatibility appendix. If I've missed any others,
please let me know where they are.
 
J

Jeff Schwab

I'm curious what the rationale was for two
oddities in C++.

I'll give you my understanding, but these may be flawed. Hopefully
someone will correct me if I'm wrong.
1) Why did the standards committee choose
the oddball no-extension names for the standard
headers (<string>, e.g.)? I can understand them wanting to
emphasize that an included entity is not necessarily
a file, but they say "header file" throughout the standard,
and calling it something like "string.hpp" would not have implied
that it was necessarily a file.

Originally, the headers were iostream.h, string.h, etc. When the
standard library got moved into the std namespace, the most
straightforward way to avoid breaking backward compatibility was to have
new headers #include the old headers within the new namespace:

/* <iostream> */
namespace std {
#include <iostream.h>
}

Anywhere, I rather prefer the shorter header names. They're easier to
read, and easier to type.
2) Why was the following use of the conditional operator
made illegal?

class B { };
class D1 : public B { };
class D2 : public B { };
int main(int argc, char **argv) {
B *p1 = argc == 1 ? new D1() : new B();
B *p2 = argc == 1 ? new D1() : new D2();
}

In the code above, the p1 assignment is legal,
while the p2 assignment is not (according to
the standard's expr.cond.5, and the Comeau
and GNU compilers I tried). Why not just
treat it as the following:

B *p2;
if (argc == 1)
p2 = new D1();
else
p2 = new D2();

The ternary operator constitutes an expression, and (unlike the if/else
tree) must have a single static type. In the case you've shown, there
is a clear an obvious choice; however, that may not always be the case.
If neither sub-expression has a type convertible to the other's, then
finding the right super-type may be non-trivial. For example:

struct A { }
struct B { };
struct D1: B { };
struct D2: B { };

int main(int argc, char** argv[]) {

D1 d1;
D2 d2;

A const* const a = argc > 2 ? &d1 : &d2;
B const* const b = argc > 2 ? &d1 : &d2;

return 0;
}
 
J

Jeff Schwab

Jeff said:
I'll give you my understanding, but these may be flawed. Hopefully
someone will correct me if I'm wrong.


Originally, the headers were iostream.h, string.h, etc. When the
standard library got moved into the std namespace, the most
straightforward way to avoid breaking backward compatibility was to have
new headers #include the old headers within the new namespace:

/* <iostream> */
namespace std {
#include <iostream.h>
}

Anywhere, I rather prefer the shorter header names. They're easier to
read, and easier to type.


The ternary operator constitutes an expression, and (unlike the if/else
tree) must have a single static type. In the case you've shown, there
is a clear an obvious choice; however, that may not always be the case.
If neither sub-expression has a type convertible to the other's, then
finding the right super-type may be non-trivial. For example:

struct A { }
struct B { };
struct D1: B { };
struct D2: B { };

// Whoops. Forgot to add the second common base class.

struct D1: A, B { };
struct D2: A, B { };

int main(int argc, char** argv[]) {

D1 d1;
D2 d2;

A const* const a = argc > 2 ? &d1 : &d2;
B const* const b = argc > 2 ? &d1 : &d2;

return 0;
}
 
B

Bo Persson

Hi,

I'm curious what the rationale was for two
oddities in C++.

1) Why did the standards committee choose
the oddball no-extension names for the standard
headers (<string>, e.g.)? I can understand them wanting to
emphasize that an included entity is not necessarily
a file, but they say "header file" throughout the standard,
and calling it something like "string.hpp" would not have implied
that it was necessarily a file.

My recollection is that there was no agreement on what extension to
use, .h, .H, .hpp, .hxx, .h++, or whatever. Some file systems don't
have extensions anyway. And besides, there is no requirement that the
standard headers should be files, so why specify file names?

So, as a compromise, just give each header a name and let it be
implementation defined what that means.


Bo Persson
 
J

James Kanze

I'll give you my understanding, but these may be flawed.
Hopefully someone will correct me if I'm wrong.
Originally, the headers were iostream.h, string.h, etc.

Originally, the names varied from one implementation to the
next: strstrea.hpp vs. strstream.h, for example. I suspect that
the absense of a suffix is mainly due to an inability to be able
to agree on what that suffix should be.
When the standard library got moved into the std namespace,
the most straightforward way to avoid breaking backward
compatibility was to have new headers #include the old headers
within the new namespace:
/* <iostream> */
namespace std {
#include <iostream.h>
}

Except that there are very definite differences in addition to
the namespace ones. To begin with, the classes in <iostream>
are all (except ios_base) templates; none of those in
<iostream.h> were. And there are other, subtle differences: the
initial values for the format flags were changed, filebuf does
code translation according to the current locale, unless you
specify otherwise, etc.

Most of the other headers didn't even exist in pre-standard C++.
Anywhere, I rather prefer the shorter header names. They're
easier to read, and easier to type.

Pragmatically, however, most implementations map them to a file
of the same name, and having file names without suffixes is a
bit of a pain. (Witness all the problems caused by the fact
that executables don't have a suffix under Unix. If I want to
clean out all of the generated files under Windows, I type "rm
*.obj *.lib *.exe". Under Unix, "rm *.o *.a *" is NOT
recommended---you have to name each of the executables
directly.)
The ternary operator constitutes an expression, and (unlike
the if/else tree) must have a single static type. In the case
you've shown, there is a clear an obvious choice; however,
that may not always be the case.

One could have imagined using the most derived common type, or
some such rules. I wouldn't like to have to formulate that in
standardese, however, and as you say, there could be cases where
it would be ambiguous.
If neither sub-expression has a type convertible to the other's, then
finding the right super-type may be non-trivial. For example:
struct A { }
struct B { };
struct D1: B { };
struct D2: B { };

Didn't you want multiple inheritance here? Otherwise, there's
no ambiguity later.
int main(int argc, char** argv[]) {

D1 d1;
D2 d2;
A const* const a = argc > 2 ? &d1 : &d2;
B const* const b = argc > 2 ? &d1 : &d2;
return 0;
}

Supposing you meant multiple inheritance, one could still
imagine something like the rule for overload resolution when
taking the address of a function. I'm not sure that's a road I
want to take, however.

When in doubt, of course, the best solution for the committee is
to ban it. You can always add support for something that was
previously illegal in a later version, if the need becomes felt,
or the situation becomes clearer. Once you've defined a
specific support, however, you can't remove it if it turns out
to be a mistake.

As far as I know, there've been no propositions to support the
above, even in a limited fashion (e.g. by saying it is an error
if there is an ambiguity). So presumably, no one has felt that
it was an important enough issue to address.
 
R

Ron Natalie

In the code above, the p1 assignment is legal,
while the p2 assignment is not (according to
the standard's expr.cond.5, and the Comeau
and GNU compilers I tried). Why not just
treat it as the following:

B *p2;
if (argc == 1)
p2 = new D1();
else
p2 = new D2();
Because you didn't write that. The ?: operator is different
than IF in that it returns a value. That value must have a
(static) type. The operation is only defined if the expression
A ? B : C
can have B converted to C (exclusive) OR C converted to B (or
one operand is a throw experssion).
 
H

Hendrik Sattler

James said:
Pragmatically, however, most implementations map them to a file
of the same name, and having file names without suffixes is a
bit of a pain. (Witness all the problems caused by the fact
that executables don't have a suffix under Unix. If I want to
clean out all of the generated files under Windows, I type "rm
*.obj *.lib *.exe". Under Unix, "rm *.o *.a *" is NOT
recommended---you have to name each of the executables
directly.)

Maybe you should start using build systems that do not mess with the source
tree. Or write proper 'clean' targets. Or get more confident about the
commands that you type ;)

HS
 
J

James Kanze

Maybe you should start using build systems that do not mess
with the source tree. Or write proper 'clean' targets. Or get
more confident about the commands that you type ;)

Or not use environments or code developed by others. Or not
have to use directory trees which I didn't write the code
for (e.g. like /usr/bin).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top