Why can't PODs have constructors?

Z

Zeppe

Victor said:
Ah... By "empty constructor" you mean "using default initialisation
semantics". OK.

Not necessarily, but it could be. I meant that providing a constructor
with arguments in C++ gives the developer the possibility of forbidding
the creation of a class or a struct with the empty constructor, which
can be not desirable when we are handling with POD.

Regards,

Zeppe
 
V

Victor Bazarov

JohnQ said:
BobR said:
[..]
struct MyRect : public virtual RECT{ [..]
~MyRect(){ this->RECT::~RECT();}

Ooo, that destructor doesn't look good at all there the way you
defined it. (Wouldn't be allowed in a POD either).

Actually, regardless of whether 'RECT' implements a d-tor or not,
this would actually give the code undefined behaviour. In any
situation the destructors of base class subobjects are called
right after the body of the destructor of this class, so here BobR
would actually cause the RECT d-tor to be called twice, which is
a VERY BAD THING(tm).

V
 
H

Howard Hinnant

Fwiw, a slight revision of
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2294.html was
voted into the C++0X working draft last week. This paper includes the
following rationale:

Which sounds like I might get the guarantee I want someday. It also implies
that I can do it now and chances are that it will work.

I have a question, based upon the link you gave. Wouldn't it be easier to
enforce structs to be PODs? That is, just disallow anything in a struct that
is a party spoiler in regards to layout and size. In still other words,
don't allow structs to be classes and let the compiler warn when an attempt
is made to declare or derive or compose illegally. As long as structs are
allowed to evolve into classes, a lot of programmer productivity goes out
the window.[/QUOTE]

I'd probably vote for that in a new language. But not in a revision to
C++. It would break too much code.

-Howard
 
J

JohnQ

Howard Hinnant said:
Which sounds like I might get the guarantee I want someday. It also
implies
that I can do it now and chances are that it will work.

I have a question, based upon the link you gave. Wouldn't it be easier to
enforce structs to be PODs? That is, just disallow anything in a struct
that
is a party spoiler in regards to layout and size. In still other words,
don't allow structs to be classes and let the compiler warn when an
attempt
is made to declare or derive or compose illegally. As long as structs are
allowed to evolve into classes, a lot of programmer productivity goes out
the window.

I'd probably vote for that in a new language. But not in a revision to
C++. It would break too much code.[/QUOTE]

Well how about leaving 'struct' alone and defining a new keyword:
'PODStruct'? Then even "no padding or aligning allowed" could be also
included in the spec making for some nice IO scenarios. Slowly, 'struct'
would be deprecated.

John
 
V

Victor Bazarov

JohnQ said:
Well how about leaving 'struct' alone and defining a new keyword:
[..]
Slowly, 'struct' would be deprecated.

I vote for "very slowly". Maybe even "very very extremely slowly".
Like, in the span of fifty years or more...

V
 
J

JohnQ

Victor Bazarov said:
JohnQ said:
BobR said:
[..]
struct MyRect : public virtual RECT{ [..]
~MyRect(){ this->RECT::~RECT();}

Ooo, that destructor doesn't look good at all there the way you
defined it. (Wouldn't be allowed in a POD either).

Actually, regardless of whether 'RECT' implements a d-tor or not,
this would actually give the code undefined behaviour. In any
situation the destructors of base class subobjects are called
right after the body of the destructor of this class, so here BobR
would actually cause the RECT d-tor to be called twice, which is
a VERY BAD THING(tm).

I didn't think a compiler-supplied destructor (does it supply one for POD
structs?) could be called.

John
 
V

Victor Bazarov

JohnQ said:
Victor Bazarov said:
JohnQ said:
[..]
struct MyRect : public virtual RECT{ [..]
~MyRect(){ this->RECT::~RECT();}

Ooo, that destructor doesn't look good at all there the way you
defined it. (Wouldn't be allowed in a POD either).

Actually, regardless of whether 'RECT' implements a d-tor or not,
this would actually give the code undefined behaviour. In any
situation the destructors of base class subobjects are called
right after the body of the destructor of this class, so here BobR
would actually cause the RECT d-tor to be called twice, which is
a VERY BAD THING(tm).

I didn't think a compiler-supplied destructor (does it supply one for
POD structs?) could be called.

What happens if later somebody rewrites 'RECT' and gives it a user-
defined destructor?

V
 
J

JohnQ

Victor Bazarov said:
JohnQ said:
Well how about leaving 'struct' alone and defining a new keyword:
[..]
Slowly, 'struct' would be deprecated.

I vote for "very slowly". Maybe even "very very extremely slowly".
Like, in the span of fifty years or more...

Even "never" would be fine. _New_ development would/should just avoid the
use of 'struct' in favor of 'class' and 'podstruct'.

John
 
J

JohnQ

Victor Bazarov said:
JohnQ said:
Victor Bazarov said:
JohnQ wrote:
[..]
struct MyRect : public virtual RECT{
[..]
~MyRect(){ this->RECT::~RECT();}

Ooo, that destructor doesn't look good at all there the way you
defined it. (Wouldn't be allowed in a POD either).

Actually, regardless of whether 'RECT' implements a d-tor or not,
this would actually give the code undefined behaviour. In any
situation the destructors of base class subobjects are called
right after the body of the destructor of this class, so here BobR
would actually cause the RECT d-tor to be called twice, which is
a VERY BAD THING(tm).

I didn't think a compiler-supplied destructor (does it supply one for
POD structs?) could be called.

What happens if later somebody rewrites 'RECT' and gives it a user-
defined destructor?

Then all the Windows code gets mucked up. ('RECT' is a Windows-ism).

John
 
B

BobR

JohnQ said:
in message...

Why the virtual inheritance?

Oops.
I was cleaning out my 'TestBench' program, and removed the wrong code
(had about 5 versions).
Here is the one I meant to post:

struct Rect : RECT{
Rect( long x1 = 0, long y1 = 0, long x2 = 10, long y2 = 10 ){
left = x1; bottom = y1; right = x2; top = y2;}
};

{ // main() or ?
using std::cout // for NG post
Rect rect( 10, 12, 22, 42 );
RECT rect1( rect );
RECT rect1a( Rect( 7, 14, 27, 34 ) );
Rect rect2;
cout<<" Rect rect.bottom="<< rect.bottom <<'\n'
<<" RECT rect1.bottom="<< rect1.bottom <<'\n'
<<" RECT rect1a.bottom="<< rect1a.bottom <<'\n'
<<" Rect rect2.top="<< rect2.top <<'\n'
<<" sizeof(rect)="<<sizeof(rect)<<'\n'
<<" sizeof(rect1)="<<sizeof(rect1)<<std::endl;
}
/* - output -
Rect rect.bottom=12
RECT rect1.bottom=12
RECT rect1a.bottom=14
Rect rect2.top=10
sizeof(rect)=16
sizeof(rect1)=16
*/

Sorry about that.

[ I don't remember what I was 'testing' with the 'virtual inherit',
something to do with "vector<RECT*> vRec(10, new MyRect(1,2,3,4));". ]
 
J

JohnQ

BobR said:
Oops.
I was cleaning out my 'TestBench' program, and removed the wrong code
(had about 5 versions).
Here is the one I meant to post:

struct Rect : RECT{
Rect( long x1 = 0, long y1 = 0, long x2 = 10, long y2 = 10 ){
left = x1; bottom = y1; right = x2; top = y2;}

How will the compiler be able to distinguish between using the default
constructor and the one above where you gave default values to all the
arguments? (This is a minor note that is off-topic for the thread).
};

{ // main() or ?
using std::cout // for NG post
Rect rect( 10, 12, 22, 42 );

Fine. Now how about:

Rect rect8(); // ambiguous
RECT rect1( rect );

Can't do that because RECT has not constructor taking a Rect arg. Are you
suggesting that initializing a struct with a struct (from the RECT& operator
of Rect) works? (My C-ismness, or forgetfulness thereof may be apparent).
RECT rect1a( Rect( 7, 14, 27, 34 ) );

Same question as above (and excuse me if I am at times unpedantic).
Rect rect2;
cout<<" Rect rect.bottom="<< rect.bottom <<'\n'
<<" RECT rect1.bottom="<< rect1.bottom <<'\n'
<<" RECT rect1a.bottom="<< rect1a.bottom <<'\n'
<<" Rect rect2.top="<< rect2.top <<'\n'
<<" sizeof(rect)="<<sizeof(rect)<<'\n'
<<" sizeof(rect1)="<<sizeof(rect1)<<std::endl;
}
/* - output -
Rect rect.bottom=12
RECT rect1.bottom=12
RECT rect1a.bottom=14
Rect rect2.top=10
sizeof(rect)=16
sizeof(rect1)=16
*/

Sorry about that.

What you should be sorry for is making me decipher your examples rather than
just stating the obvious (which I think I "summed up" in a near previous
post in this thread (kind of))! :p ;)

John
 
B

BobR

JohnQ said:
How will the compiler be able to distinguish between using the default
constructor and the one above where you gave default values to all the
arguments? (This is a minor note that is off-topic for the thread).

The one above is the only constructor (once you declare/define one, the
compiler does not). It seconds as the default constructor (can be called
with no args).
Fine. Now how about:

Rect rect8(); // ambiguous

Declaration of function 'rect8' which takes no parameters, and returns an
'Rect'.
You meant:
Rect rect8; // default construct, like below (rect2)

Same with built-in types:
int number();
number = 43;
// error: assignment of function `int number()'
// error: cannot convert `int' to `int ()()' in assignment

int number2( 34 ); // or: int number2;
number2 = 43; // ok
Can't do that because RECT has not constructor taking a Rect arg. Are you
suggesting that initializing a struct with a struct (from the RECT& operator
of Rect) works? (My C-ismness, or forgetfulness thereof may be apparent).

Remember it's an POD, the compiler supplied copy-ctor is fine.
And destructor, assignment operator. As soon as you define one of the three,
you should supply all three.
Same question as above (and excuse me if I am at times unpedantic).

Same answer as above.
What you should be sorry for is making me decipher your examples rather than
just stating the obvious (which I think I "summed up" in a near previous
post in this thread (kind of))! :p ;)
John

"summed up", the answer is "no".

You seem reluctant to believe that POD struct/class have the guts supplied.
Try this:

#include <iostream>

struct Bint{ int x; }; // don't come much 'plainer'

int main(){
Bint bint1; // default construct
bint1.x = 3;
Bint bint2;
bint2 = bint1; // assignment
Bint bint3( bint1 ); // copy-ctor
std::cout<<"bint1.x="<<bint1.x<<'\n';
std::cout<<"bint2.x="<<bint2.x<<'\n';
std::cout<<"bint3.x="<<bint3.x<<'\n';
return 0;
}
/* -out-
bint1.x=3
bint2.x=3
bint3.x=3
*/
 
J

JohnQ

BobR said:
The one above is the only constructor (once you declare/define one, the
compiler does not). It seconds as the default constructor (can be called
with no args).

A compiler won't generate a default constructor if any other constructor is
defined? That's news to me. And the constructor with args can serve as the
default constructor. News to me again.
Declaration of function 'rect8' which takes no parameters, and returns an
'Rect'.
You meant:
Rect rect8; // default construct, like below (rect2)

Yes, I meant that.


I'll digest the rest of the post after the weekend. (Dang my head hurts.. I
better go get a glass of wine (aka, hair of the dog). ;) )

John
 
B

BobR

JohnQ said:
I'll digest the rest of the post after the weekend. (Dang my head hurts.. I
better go get a glass of wine (aka, hair of the dog). ;) )
John

Remember, it's white wine with printed hard copy code(stains less), and red
wine with C++ (computer)(keep it away from keyboards. hard experience.).
 
J

James Kanze

Why? A constructed POD behaves the same way as a 'C struct' (almost),
it's just initialized properly, e.g.
struct Point {
Point(): x(0), y(0) {}
Point (int xcoord, int ycoord): x(xcoord), y(ycoord) {}
int x;
int y;
};
int main() {
Point p1, p2 (7, 9);
p2 = p1;
Point p3 (p2);
// Point p4 = { 1, 2 }; // error
Point a[99];
}

Once constructed, yes. The difference is in the construction: a
POD is an agglomerate (although not all agglomerates are POD's),
and uses agglomerate initialization syntax, e.g.:

Point z = { 1, 2 } ;

Especially useful, of course, is the fact that a POD can be
statically initialized (and so is exempt from order of
initialization issues). A POD can also be a member of a union.

The basic idea behind this, of course, is that if there is a
user defined constructor (actually, if there is a
non-trivial constructor), the compiler will call it, anytime
there is an object of the type. Calling it means that
initialization is no longer static, and having to call it means
that the object cannot be a member of a union (since the
compiler cannot know a priori which constructor to call).
 
J

James Kanze

[ ... ]
I guess that introducing such a requirement (to analyse the constructor
for containing constant expressions only, both in the initialiser list
and on the right-hand side of assignments in the body, and no side
effects in the body as well) would put an undue strain on the compiler
implementors.
I rather doubt it'd be much strain at all -- I'm pretty sure virtually
every reasonably self-respecting compiler's optimizer already detects
such things anyway.

It's a little bit tricker. Consider something like:

class Point {
public:
Point( int x, int y ) : myX( x ), myY( y ) {}
int myX ;
int myY ;
} ;
extern int f() ;
int i = f() ;
Point p( 2, 3 ) ;

int
f()
{
return p.myX ;
}

This code is guaranteed to initialize i with 0. If the compiler
converts the initialization of p to static initialization, it
most likely would initialize i with 2.
 
J

James Kanze

"Howard Hinnant" <[email protected]> wrote in message

[...]
I seem to remember once seeing an explination by Stroustrup of
why he didn't do this. I've forgotten where, and all of the
details, but it did seem clear to me that the current situation
is the result of a careful and reasoned decision. One may not
agree with the decision (I'm not sure I do either), but there's
certainly nothing unreasonable about it; it's one of those cases
where there are arguments on both sides, and either choice is,
in some ways, right.
Well how about leaving 'struct' alone and defining a new keyword:
'PODStruct'? Then even "no padding or aligning allowed" could be also
included in the spec making for some nice IO scenarios. Slowly, 'struct'
would be deprecated.

You certainly don't want to ban padding. That would make C++
unacceptably slow on just about every modern machine.

And of course, the layout in a struct or a class in C++ has
absolutely nothing to do with IO, in any way, shape, form or
fashion.
 
J

James Kanze

PODs do have Ctor, copy-Ctor, and assignment operators.
Otherwise you would not be able to use them in std containers
(like std::vector). You just can't write your own and keep it
a POD ('C'). (IMHO.)

Correct. To be a POD, there must be a trivial default
constructor, copy constructor, assignment operator and
destructor. A user defined version is never trivial. (Note
however that the absense of a user defined version doesn't
guarantee triviality.)
A little extra step can do that. This uses a 'RECT' from
windows. It's a POD 'C' struct with 4 longs. Try this with
your own POD struct.
struct MyRect : public virtual RECT{
MyRect( long x1 = 0, long y1 = 0, long x2 = 10, long y2 = 10 ){
left = x1; // init the RECT members
bottom = y1;
right = x2;
top = y2;
}
~MyRect(){ this->RECT::~RECT();}
RECT Rect(){ return *this;}
};
{ // main or ?
using std::cout; // for NG post
MyRect rect( 10, 12, 22, 42 );
cout<<" MyRect rect.bottom="<< rect.bottom << std::endl;
RECT rect1 = rect.Rect();
cout<<" RECT rect1.bottom="<< rect1.bottom << std::endl;

cout<<" sizeof(rect)="<<sizeof( rect )<<std::endl;
cout<<" sizeof(rect1)="<<sizeof( rect1 )<<std::endl;}
/* - output -
MyRect rect.bottom=12
RECT rect1.bottom=12
sizeof(rect)=20
sizeof(rect1)=16 // sliced back to POD
// (sixeof long == 4 on my sys)
*/
Of course it would take something a little more complex to
justify the extra layer, or you would just do:
RECT rect2 = { 14, 19, 25, 55};

I do exactly the same in some particular cases. On one hand,
you want to be able to write things like "return MyRect(...);",
without having to declare a variable. On the other, in specific
contexts, I needed to ensure static initialization, in order to
avoid order of initialization issues.
 
J

James Kanze

JohnQ said:
BobR said:
[..]
struct MyRect : public virtual RECT{ [..]
~MyRect(){ this->RECT::~RECT();}
Ooo, that destructor doesn't look good at all there the way you
defined it. (Wouldn't be allowed in a POD either).
Actually, regardless of whether 'RECT' implements a d-tor or not,
this would actually give the code undefined behaviour. In any
situation the destructors of base class subobjects are called
right after the body of the destructor of this class, so here BobR
would actually cause the RECT d-tor to be called twice, which is
a VERY BAD THING(tm).

Except that in this case, we know that RECT has a trivial
destructor, so "calling" it has no effect.
 
B

BobR

JohnQ wrote in message...
struct Rect : RECT{
Rect( long x1 = 0, long y1 = 0, long x2 = 10, long y2 = 10 ){
left = x1; bottom = y1; right = x2; top = y2;}
};
[snip]
A compiler won't generate a default constructor if any other constructor is
defined? That's news to me. And the constructor with args can serve as the
default constructor. News to me again.

struct MyRect2{ // test compiler supplied default Ctor
long x, y;
MyRect2( long x1, long y1 ): x(x1), y(y1){}
// MyRect2( long x1=0, long y1=0 ): x(x1), y(y1){} // no error
// (only (un-)comment one at a time)(or comment both to test)
};

MyRect2 Rect2;
// error: no matching function for call to `MyRect2::MyRect2()'
// error: candidates are: MyRect2::MyRect2(const MyRect2&)
// : MyRect2::MyRect2(long int, long int)

MyRect2 Rect2a(1,2); // ok (either Ctor shown )

So, that shows you that once *you* declare/define anything that can be taken
as a constructor, the compiler will *not* supply one.
Try it, experiment.
 

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

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top