Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to get recursion in constructor?

184 views
Skip to first unread message

Krzysztof Żelechowski

unread,
Aug 31, 2005, 10:54:01 AM8/31/05
to
My dear expert friends,

How can I correct the following code to make it compile under C++? Plz help
coz I'm stuck.

Chris

class X {

int const m;
public:
X(int);

X(int a, int b): X(a + b) {

// do the same thing as X(a + b) does without knowing what it X(int) does

}

};


I can think of solving this problem by splitting the class into two:

class Xb {
public:
int const m;
Xb(int);
};

class X: public Xb {
X(int a): Xb(a) {
}
X(int a, int b): Xb(a + b) {
}
};

However, this is very clumsy. On the other hand, the named constructor
idiom allows recursion but my class is a template argument and I cannot use
the named constructor in the template code. And there is no way back: you
cannot get X::X(int, int) from static X X::create(int, int). Or can you?


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Jens Seidel

unread,
Sep 1, 2005, 5:17:04 AM9/1/05
to
Krzysztof =AFelechowski wrote:

> class X {
>
> int const m;
> public:
> X(int);
>
> X(int a, int b): X(a + b) {
>

> // do the same thing as X(a + b) does without knowing what it X(int) do=
es

This is valid in C# but not in C++.

> I can think of solving this problem by splitting the class into two:
>

> However, this is very clumsy.

Right.

Why not just using an init function?
private:
void init(int a, int b=0)

and calling init() from X(int) resp. X(int, int)?

Jens

Rob

unread,
Sep 1, 2005, 5:19:52 AM9/1/05
to
Krzysztof /elechowski wrote:

Two alternatives I can think of;

First is to use a helper function;

class X
{
public:
X(int a) {initalise(a);};
X(int a, int b) {initialise(a+b);};
private:
void initialise(int);
};

The second is to use default arguments;

class X
{
public:
X(int a, int b = 0)
{
// initialise in terms of a+b
};
};

Kelly Walker

unread,
Sep 1, 2005, 5:24:49 AM9/1/05
to

"Krzysztof Żelechowski" <kri...@qed.pl> wrote in message
news:df456i$l6r$1...@node1.news.atman.pl...

> My dear expert friends,
>
> How can I correct the following code to make it compile under C++? Plz
> help
> coz I'm stuck.
>
> Chris
>
>
>
> class X {
>
> int const m;
> public:
> X(int);
>
> X(int a, int b): X(a + b) {
>
> // do the same thing as X(a + b) does without knowing what it X(int) does
>
> }
>
> };
>
>
> I can think of solving this problem by splitting the class into two:
>
> class Xb {
> public:
> int const m;
> Xb(int);
> };
>
> class X: public Xb {
> X(int a): Xb(a) {
> }
> X(int a, int b): Xb(a + b) {
> }
> };
>
> However, this is very clumsy. On the other hand, the named constructor
> idiom allows recursion but my class is a template argument and I cannot
> use
> the named constructor in the template code. And there is no way back: you
> cannot get X::X(int, int) from static X X::create(int, int). Or can you?
>
>

This may only satisfy your simplified example, but what about using a
private initialization function as in:

class X {
public:
X(int a) {

init(a);
}

X(int a, int b) {
init(a + b);
}

private:
init(int i) {...}
};

-Kelly

Tony Delroy

unread,
Sep 1, 2005, 7:17:05 AM9/1/05
to
Another way is:

X::X(int a, int b) { *this = X(a + b); }

Tony

Tony Delroy

unread,
Sep 2, 2005, 7:07:56 AM9/2/05
to
Oh, BTW: your topic title "recursion in constructors" is a misnomer.
Calling one overloaded function from another isn't recursion.

Plus, something I didn't bother to mention earlier... the assignment
technique can only be used in the function body, so any default
construction done in the initialiser list may be wasted, and indeed
need to be torn down before the new values can be copy-constructed.
Still, an optimiser may be able to remove this overhead.

Christopher Yeleighton

unread,
Sep 2, 2005, 7:03:51 AM9/2/05
to

"Jens Seidel" <Jens....@imkf.tu-freiberg.de> wrote in message
news:df65ug$2tef$1...@nserver.hrz.tu-freiberg.de...

> Krzysztof =AFelechowski wrote:
>
>> class X {
>>
>> int const m;
>> public:
>> X(int);
>>
>> X(int a, int b): X(a + b) {
>>
>> // do the same thing as X(a + b) does without knowing what it X(int) do=
> es
>
> This is valid in C# but not in C++.
>
>> I can think of solving this problem by splitting the class into two:
>>
>> However, this is very clumsy.
>
> Right.
>
> Why not just using an init function?
> private:
> void init(int a, int b=0)
>
> and calling init() from X(int) resp. X(int, int)?
>
> Jens

Won't do coz m is const.
Chris

Christopher Yeleighton

unread,
Sep 2, 2005, 7:11:00 AM9/2/05
to

"Tony Delroy" <tony_i...@yahoo.co.uk> wrote in message
news:1125568114.9...@o13g2000cwo.googlegroups.com...

> Another way is:
>
> X::X(int a, int b) { *this = X(a + b); }
>
> Tony
>

Won't do coz m is const.
Chris

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Christopher Yeleighton

unread,
Sep 2, 2005, 7:11:42 AM9/2/05
to

"Rob" <nos...@nonexistant.com> wrote in message
news:4316...@duster.adelaide.on.net...

Won't do coz m is const.
Chris

> The second is to use default arguments;


>
> class X
> {
> public:
> X(int a, int b = 0)
> {
> // initialise in terms of a+b
> };
> };
>

Won't do if X(int a): m(a + 01) {}
Thanx for your time
Chris

Christopher Yeleighton

unread,
Sep 2, 2005, 7:10:15 AM9/2/05
to

"Kelly Walker" <kelly_g...@earthlink.net> wrote in message
news:FKrRe.5198$FW1...@newsread3.news.atl.earthlink.net...


Won't do coz m is const. Does this ultimately mean that keeping references
as class members is a no-no?
Chris

j_ri...@gmx.de

unread,
Sep 2, 2005, 10:30:11 AM9/2/05
to
Krzysztof Żelechowski schrieb:

> class X {
>
> int const m;
> public:
> X(int);
>
> X(int a, int b): X(a + b) {
> // do the same thing as X(a + b) does without knowing what it X(int) does
> }
> };

You can write a separate function which returns your m:

class X
{
int const m;

static int calculate( int );

public:
X( int a )
: m( calculate( a ) )
{}

X( int a, int b )
: m( calculate( a + b ) )
{}
};


Jörg

Gene Bushuyev

unread,
Sep 2, 2005, 12:39:30 PM9/2/05
to
"Krzysztof Żelechowski" <kri...@qed.pl> wrote in message
news:df456i$l6r$1...@node1.news.atman.pl...
> My dear expert friends,
>
> How can I correct the following code to make it compile under C++? Plz help
> coz I'm stuck.
>
> Chris
>
>
>
> class X {
>
> int const m;
> public:
> X(int);
>
> X(int a, int b): X(a + b) {
>
> // do the same thing as X(a + b) does without knowing what it X(int) does
>
> }
>
> };

There is no problem, or better say the problem is contrived. There is no need in
second constructor, the clients should call X(a+b). When you decide that one
ctor needs to call another, stop and think which constructor is actually needed,
you definitely don't need both.
Having said that, it is possible that two legitimate constructors need to share
the same functionality. In such a case, the class is a candidate for splitting
to two, as there is obviously some non-trivial functionality that deserves to be
in a separate base class.

- gene

P.S. "init" functions are evil, as well as calling copy assignment in ctor.

Tony Delroy

unread,
Sep 2, 2005, 5:48:46 PM9/2/05
to
Ahhh... I'd overlooked that detail. Well, you have to choose between
using amenable data types (e.g. not references, not constants) that can
be set after the initialiser list, and the inconvenience of explicitly
writing each constructor. - Tony

Ali Çehreli

unread,
Sep 3, 2005, 5:28:21 AM9/3/05
to
"Tony Delroy" <tony_i...@yahoo.co.uk> wrote in message
news:1125568114.9...@o13g2000cwo.googlegroups.com...
> Another way is:
>
> X::X(int a, int b) { *this = X(a + b); }

Won't work.

The object on the left hand side of operator= is not constructed at the time
you call the assignment. Yes, some or all of its members are default
initialized, but the object is not.

By definition, the object on the left hand side will be constructed when the
constructor completes.

Ali

Martin Stettner

unread,
Sep 3, 2005, 2:52:22 PM9/3/05
to

"Christopher Yeleighton" <kri...@qed.pl> schrieb im Newsbeitrag
news:df71qn$o4b$1...@sklad.atcom.net.pl...

>
> "Jens Seidel" <Jens....@imkf.tu-freiberg.de> wrote in message
> news:df65ug$2tef$1...@nserver.hrz.tu-freiberg.de...
>> Krzysztof =AFelechowski wrote:
>>
>>> class X {
>>>
>>> int const m;
>>> public:
>>> X(int);
>>>
>>> X(int a, int b): X(a + b) {
>>>
>>> // do the same thing as X(a + b) does without knowing what it X(int) do=
>> es
>>
>> This is valid in C# but not in C++.
>>
>>> I can think of solving this problem by splitting the class into two:
>>>
>>> However, this is very clumsy.
>>
>> [snip]

>
> Won't do coz m is const.
> Chris

Perhaps you can write a function that computes the value of m from given the
argument:

protected:
int computeM(int);

(or even static int computeM(int);)

Then write:

X(int a) : m(computeM(a)) { /* ... */ }
X(int a, int b): m(computeM(a,b)) { /* ... */ }

greetings
Martin

vadim alexandrov

unread,
Sep 3, 2005, 11:24:10 PM9/3/05
to
Probably what you are asking for is:

X::X(int a, int b ) {

this->~X(); // destroy itself
new (this) X( a+b ); // placement new
}

But it doesn't look nice.
You can ommit calling destructor first, if members are primitive types
only.

(Sorry, I don't have C++ compiler here to check this code fully)

Vadim Alexandrov

Marc Mutz

unread,
Sep 3, 2005, 11:37:50 PM9/3/05
to
Gene Bushuyev wrote:
<snip>
> P.S. "init" functions are evil
<snip>

It might be that I'm born yesterday again, but: "Hu?"
Care to explain?

Thanks,
Marc

Frank Chang

unread,
Sep 5, 2005, 7:15:33 AM9/5/05
to
Vadim, I should add the original poster said that the constructor X(int
a) was a "black box" and we need not know what it does. But suppose the
constructor X(int a) opens a socket or a database connection, then we
could not utilize a temporary object created by the X(int a)
constructor, as shown in my previous post because it would be
immedately destroyed after the const member initialization:

X(int a, int b)
:m(X(a+b).m)
{}

So I am changing it to

X(int a, int b )

:m((new X(a+b))->m),
{}


so that resources allocated in the X(int a) constructor could be closed
or deallocated in the destructor ~X() when X goes out of scope or is
deleted.

The class X nows looks like:
class X
{
private:
int const m;
public:
X(int a);

X(int a, int b )

:m((new X(a+b))->m)
{}

~X();
};

Thank you.


class X
{
private:
int const m;
public:
X(int a);

X(int a, int b )

:m((new X(a+b))->m),
{}

~X();
};

Frank Chang

unread,
Sep 5, 2005, 7:20:31 AM9/5/05
to
Vadim, Your suggested code,

X::X(int a, int b ) {
this->~X(); // destroy itself
new (this) X( a+b ); // placement new
}

does not compile. The error message I get is: 'X::m' : must be
initialized in constructor base/member initializer list. In the ARM ,
Margaret Ellis and Bjorne Stroustrup state that the const member
variables , such as X::m, can only be initialized in the member
initialization list:
However, your idea is still very good if we change it to:

class X
{
private:
int const m;
public:
X(int a)

:m(a)
{}

X::X(int a, int b )

:m(X(a+b).m)
{}
};

Thank you.

msalters

unread,
Sep 5, 2005, 7:44:13 AM9/5/05
to

Marc Mutz schreef:

> Gene Bushuyev wrote:
> <snip>
> > P.S. "init" functions are evil
> <snip>
>
> It might be that I'm born yesterday again, but: "Hu?"
> Care to explain?

/Public/ init functions that /must/ be called are evil - they
should be replaced by constructors that actually construct.

HTH,
Michiel Salters

Marc Mutz

unread,
Sep 5, 2005, 7:40:43 AM9/5/05
to
Forget this suggestion immediately :)
Don't even look at it.

vadim alexandrov wrote:
<snip>


> Probably what you are asking for is:
>
> X::X(int a, int b ) {
> this->~X(); // destroy itself
> new (this) X( a+b ); // placement new
> }
>
> But it doesn't look nice.
> You can ommit calling destructor first, if members are
> primitive types only.
>
> (Sorry, I don't have C++ compiler here to check this
> code fully)

<snip>

Ok you did nonetheless. What's going on here is that this
code invoked the dtor on a non-initialized object.
Initialization of an object is successful only after the
ctor returns sucessfully. Only then may you call the dtor
on the object.

Marc

vadim alexandrov

unread,
Sep 6, 2005, 6:50:46 AM9/6/05
to
class X
{
private:
int const m;
public:
X(int a);
X(int a, int b )
:m((new X(a+b))->m), // memory leak here
{}
~X();
};

consider:

static char sTmpX[ sizeof( X ) ];

class X {
private:
int const m;
public:
X(int a);
X(int a, int b )

:m( (new(sTmpX) X(a+b))->m ), // should work
{}
~X();
};

It is not nice.
It doesn't call destructor on sTmpX.
It is not thread-safe.

Think of class redesign.

Frank Chang

unread,
Sep 6, 2005, 9:00:40 PM9/6/05
to
Vadim, I posted this answer first on Sept 5, 2005. Perhaps you didn't
see it:

class X
{
private:
int const m;
public:
X(int a);

X(int a, int b )

:m(X(a+b).m) // no memory leak but unnamed temporary object X
{}

~X();

};


Thank you.

werasmus

unread,
Sep 6, 2005, 9:09:39 PM9/6/05
to

vadim alexandrov wrote:
> consider:
>
> static char sTmpX[ sizeof( X ) ];
>
> class X {
> private:
> int const m;
> public:
> X(int a);
> X(int a, int b )
> :m( (new(sTmpX) X(a+b))->m ), // should work
> {}
> ~X();
> };

How about:

class X
{
public:
X(int a): m(a){ ; }
X(int a, int b)
: m(makeFrom(X(a+b)).m)
{
}

private:
//Default required for X instantiated in makeFrom
X():m(0){ ; }

static X const& makeFrom(X const& other)
{
//Will only be instantiated the first time makeFrom is called,
// right?
static X x;
x.m = other.m;

// or... x = other;
}

int const m;
};

Frank Chang

unread,
Sep 7, 2005, 10:04:49 AM9/7/05
to
werasmus, I get compilation errors when I try to compile your version
of class X: 1) X: no appropriate default constructor available 2)
l-value requires const object. Also, I don't see a return ??? in your
static makeFrom(X const& other) function. Thank you.

werasmus

unread,
Sep 7, 2005, 3:09:55 PM9/7/05
to

werasmus wrote:
> How about:
>
> class X
> {
> public:
> X(int a): m(a){ ; }
> X(int a, int b)
> : m(makeFrom(X(a+b)).m)
> {
> }
>
> private:
> //Default required for X instantiated in makeFrom
> X():m(0){ ; }
>
> static X const& makeFrom(X const& other)
> {
> //Will only be instantiated the first time makeFrom is called,
> // right?
> static X x;
> x.m = other.m;
>
> // or... x = other;
> }
>
> int const m;
> };

Sorry, somehow missed the int <const> m... bad. Then above would not
work, of course (besides not being thread safe). Looking at the
original problem, why can't the instantiator not just do:

X x(1+1);

This then calls the only constructor, which BTW could be explicit. Or
how about having:

class X
{ //... includes int const m et. all.
public:
X& operator+=( const X& rhs );
};
const X operator+( const X& lhs, const X& rhs )
{
return( X(lhs) += rhs );
}

Now we could instantiate X as:

X x( X(10)+X(20) );

For this example though, why the bother:

X x(1+1)...
... still rules

Regards,

Werner

Frank Chang

unread,
Sep 8, 2005, 5:13:59 AM9/8/05
to
Werner, I've certainly made my share of mistakes on this bulletin
board. I understand that many people making posts do not have a
compiler with them when they make their posts. About your idea,

class X
{ //... includes int const m et. all.
public:
X& operator+=( const X& rhs );
};


const X operator+( const X& lhs, const X& rhs )
{
return( X(lhs) += rhs );
}

Now we could instantiate X as:

X x( X(10)+X(20) );


This will run OK as long as the X::X(int a) constructor follows the
invariant :
X(a + b) = X(a) + X(b) . But , the original poster told we should make
no assumptions about what the constructor X(int a) does. Thank you.

Christopher Conrade Zseleghovski

unread,
Sep 8, 2005, 5:20:17 AM9/8/05
to
werasmus <w_e...@telkomsa.net> wrote:
>
> Sorry, somehow missed the int <const> m... bad. Then above would not
> work, of course (besides not being thread safe). Looking at the
> original problem, why can't the instantiator not just do:
>
> X x(1+1);
>
> This then calls the only constructor, which BTW could be explicit. Or
> how about having:
>
> class X
> { //... includes int const m et. all.
> public:
> X& operator+=( const X& rhs );
> };
> const X operator+( const X& lhs, const X& rhs )
> {
> return( X(lhs) += rhs );

Error: no constructor available for X(lhs)
Error: a temporary cannot be used where a reference is needed

Christopher Conrade Zseleghovski

unread,
Sep 8, 2005, 5:21:27 AM9/8/05
to
werasmus <w_e...@telkomsa.net> wrote:
>
> How about:
>
> class X
> {
> public:
> X(int a): m(a){ ; }
> X(int a, int b)
> : m(makeFrom(X(a+b)).m)
> {
> }
>
> private:
> //Default required for X instantiated in makeFrom
> X():m(0){ ; }
>
> static X const& makeFrom(X const& other)
> {
> //Will only be instantiated the first time makeFrom is called,
> // right?
> static X x;
> x.m = other.m;

Error: X::m is constant

werasmus

unread,
Sep 9, 2005, 11:25:56 AM9/9/05
to

Frank Chang wrote:
> Werner, I've certainly made my share of mistakes on this bulletin
> board. I understand that many people making posts do not have a
> compiler with them when they make their posts.

Yes, I did not explicitly compile the code. I forgot that when you have
a const member, the default assignment operator is not adequate. The
default copy constructor does seem adequate, though. The 2nd solution I
have given would also fail:
class X{ //...


X& operator+=( const X& rhs );
};

is not possible as <X& operator=> would fail. Therefore operator+ could
not be implemented in terms of operator+=. Operator+ can be implemented
by doing this though:

X operator( const X& lhs, const X& rhs)
{
return X( lhs.getm()+rhs.getm() );
}

> This will run OK as long as the X::X(int a) constructor follows the
> invariant :
> X(a + b) = X(a) + X(b).

Yes, agree:

X( int a ):m_( 1000*a){ ; } //I made assumptions...you win again
X( int a, int b ): m_( X(a+b).m_{ ; } //Good idea ;-), Thanks

Once again, I'm writing without compiling (not entirely). Pardon any
mistakes. I think we are now in agreement. The not being able to assign
when a const member exists have caught me more than once in the past.

Kind regards (Appreciate for your patience/pardon),

Werner

Tony Delroy

unread,
Sep 9, 2005, 8:33:18 PM9/9/05
to
The object on the left hand side has passed the
default-initialisation/initialisation-list stage of its construction,
so the only step left in construction is for the constructor body to
execute. Given this is the body, what can go wrong? I haven't
pondered the Standard to see if this is undefined, but think there's
enough well defined behaviour in the knowledge that the implicit
construction steps should have completed before the constructor body,
to make the technique practical for use. Is there a compiler it
doesn't work on? Thanks, Tony

Frank Chang

unread,
Sep 9, 2005, 8:41:47 PM9/9/05
to
Werner, Ah, words of flattery, which I am not worthy of. I guess we
have to wait until the original poster informs us what the correct
solution is. Thank you

vadim alexandrov

unread,
Sep 10, 2005, 9:35:54 AM9/10/05
to
That should work too.

But, if say constructor opens a socket, then destructor should close
it.
The value of m may be irrelevant,

Why not redesign classs:

struct Y : public X {
Y( int x ) : X( x ) {}
Y( int x, int y ) : X( x+y ) {}
};

Code fix:

X::X(int a, int b ) : m( -1 ) {


this->~X(); // destroy itself
new (this) X( a+b ); // placement new
}

Vadim

?utf-8?Q?Ali_=C3=87ehreli?=

unread,
Sep 11, 2005, 11:17:18 AM9/11/05
to
I want to quote some of the earlier lines to give context to the discussion:

You had said:

<quote>


> Another way is:
>
> X::X(int a, int b) { *this = X(a + b); }

</quote>

Then I had said:

<quote>


The object on the left hand side of operator= is not constructed at the time
you call the assignment. Yes, some or all of its members are default
initialized, but the object is not.

By definition, the object on the left hand side will be constructed when the
constructor completes.

</quote>

"Tony Delroy" <tony_i...@yahoo.co.uk> wrote in message

news:1126272833.5...@g44g2000cwa.googlegroups.com...


> The object on the left hand side has passed the
> default-initialisation/initialisation-list stage of its construction,
> so the only step left in construction is for the constructor body to
> execute. Given this is the body, what can go wrong? I haven't
> pondered the Standard to see if this is undefined, but think there's
> enough well defined behaviour in the knowledge that the implicit
> construction steps should have completed before the constructor body,
> to make the technique practical for use. Is there a compiler it
> doesn't work on? Thanks, Tony

After thinking more about this, I agree with you that nothing can go wrong
with this particular case; or even in general, provided that the assignment
is the last line of the constructor body:

X::X(/* arguments */)
: /* the first part of the initialization */
{
/* the rest of the initialization */

// the last line of the constructor:
*this = X(/* different arguments */);

// WARNING: Don't add any lines here
}

There is nothing left for the constructor to do after the constructor body;
once we are in there, the final part of the construction is left to us.

I would be very uncomfortable to leave the safety of the object to warning
comments though. And no matter what, it feels wrong to construct an object
by

1) construct with one of the constructors

but, before leaving the body completely,

2) construct a temporary using another constructor

and finally

3) assign to this object from the temporary

The temporary in step 2 will require a destruction.

Since assignment is inherently a construction and a destruction, it adds to
the number of constructors and destructors (though those may be optimized
away). So, in addition to the uneasy feeling of assigning to a
not-completely-constructed object, it is also wasteful.

I agree though that this could be a suitable idiom for special cases like
the OP's question.

Ali

Frank Chang

unread,
Sep 11, 2005, 11:32:22 AM9/11/05
to
Vadim, Your idea is quite good. What if the value of m were relevant?
How would you handle that? My opinion is that both constructors X(int
a) and X(int a, int b) should be independently responsible for opening
sockets. What is your opinion? Thank you.

werasmus

unread,
Sep 12, 2005, 5:29:27 AM9/12/05
to
Frank Chang wrote:
> Werner, Ah, words of flattery, which I am not worthy of. I guess we
> have to wait until the original poster informs us what the correct
> solution is. Thank you

Hi Frank,

I Wonder whether the original poster will inform us of the correct
solution. Yes, he may yet be smiling at all our clutching at air at the
momemt. I have some new comments on your solution:

Your solution in (34):
X::X( int a, int b )
: m_( X( a+b ).m_{ }

What if the construction of X was very expensive? Every class could
use individual ways to solve this problem, depending on the physical
structure of the class.

A nice general solution may be to revert to using the pimpl idiom.

//X.h
class X
{
public:
X( int a );
X( int a, int b );

private:
struct Impl;
std::auto_ptr<Impl> const pimpl_;

};

//X.cpp
struct X::Impl
{
Impl( int m ) : m_( m ){ }

int const m_;
};

X::X( int a )
: pimpl_( new Impl( a ) )
{ ; }

X::X( int a, int b )
: pimpl_( new Impl( a+b ) )
{ ; }

Now matter how expensive construction of Impl, it is only required to
be constructed once. Its constructor also only requires one argument
as the interface can encapsulate how this argument should be passed
i.e. the interface may make assumptions about its implementation, as
the implementation is the encapsulation of the interface - for example:

: pimpl_( new Impl( a*MySpecialFactor ) ){} //and
: pimpl_( new Impl( (a+b)*MySpecialFactory ){}

The initialisation of Impl can also change without effecting the
interface.

Regards,

W

Branimir Maksimovic

unread,
Sep 12, 2005, 8:24:14 PM9/12/05
to

Krzysztof Żelechowski wrote:
>
>
>
> class X {
>
> int const m;
> public:
> X(int);
>
> X(int a, int b): X(a + b) {
>
> // do the same thing as X(a + b) does without knowing what it X(int) does
>
> }
>
> };

>
>
> I can think of solving this problem by splitting the class into two:
>
> class Xb {
> public:
> int const m;
> Xb(int);
> };
>
> class X: public Xb {
> X(int a): Xb(a) {
> }
> X(int a, int b): Xb(a + b) {
> }
> };

>
> However, this is very clumsy.

You can't call constructor in C++, constructors are called implicitly
as
side effect of objects creation.
If you don't like splitting class in two, you can construct recursive
structure of objects:

class X {
auto_ptr<X> p;
int const m;
public:
X(int i):p(0),m(i){}

X(int a, int b): p(new X(a + b)), m(0) {

// do the same thing as X(a + b) does without knowing what it X(int)

does
}
X(int a, int b, int c): p(new X(a*b,a*c)), m(0){
}
X(const X& in): p(in.p.get()?new X(*in.p):0), m(in.m) {
}
X& operator=(const X& in){
if(p.get() && in.p.get()) *p=*in.p;
// whatever
return *this;
}
// ~X(){}
void f(int i){ if (p.get()) p->f(i); else /* code */; }
};

This way you can remember how object is constructed :) and do some
exotic things if needed.

Greetings, Bane.

Frank Chang

unread,
Sep 12, 2005, 8:30:56 PM9/12/05
to
Werner, I compiled your code and tested it briefly. I have a question
about your implementation of the Pimpl pattern in the class X. I was
wondering why you used an auto_ptr to hold the pimpl pointer instead of
a raw pointer. The only reason I can think of it is that you wanted to
avoid the shallow copy/deep copy problem in X's copy constructor and
X's assignment operator. However, using the auto_ptr to hold a pimpl
pointer is problematic because of the potential for transfer of
ownership issues. Have you looked at the boost::shared_ptr class? It
might help you avoid some problems with auto_ptr's. Thank you

kanze

unread,
Sep 13, 2005, 5:09:06 AM9/13/05
to
Frank Chang wrote:

> Werner, I compiled your code and tested it briefly. I have a
> question about your implementation of the Pimpl pattern in the
> class X. I was wondering why you used an auto_ptr to hold the
> pimpl pointer instead of a raw pointer.

I missed earlier parts of this thread, but one thing is certain:
you cannot use an std::auto_ptr for the compilation firewall
idiom. It's undefined behavior to instantiation
std::auto_ptr<T> if T is an incomplete type. (See §17.4.3.6/2.)

> The only reason I can think of it is that you wanted to avoid
> the shallow copy/deep copy problem in X's copy constructor and
> X's assignment operator. However, using the auto_ptr to hold
> a pimpl pointer is problematic because of the potential for
> transfer of ownership issues. Have you looked at the
> boost::shared_ptr class? It might help you avoid some
> problems with auto_ptr's.

I don't think that boost::shared_ptr is appropriate either,
unless you're trying to do some sort of copy on write. The fact
that a class uses the compilation firewall idiom should be
transparent to the user -- using reference semantics (shallow
copy) rather than value semantics is anything but transparent.

IMHO, boost's scoped_ptr would be the only appropriate smart
pointer, although given the simplicity of the problem, I'm not
sure that a smart pointer is necessary, or even appropriate.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

werasmus

unread,
Sep 13, 2005, 5:05:45 AM9/13/05
to

Frank Chang wrote:
> Werner, I compiled your code and tested it briefly. I have a question
> about your implementation of the Pimpl pattern in the class X. I was
> wondering why you used an auto_ptr to hold the pimpl pointer instead of
> a raw pointer. The only reason I can think of it is that you wanted to
> avoid the shallow copy/deep copy problem in X's copy constructor and
> X's assignment operator.

Yes, the auto_ptr is const - therefore it cannot be used for transfer
of ownership. Also - I could probably explicitly have inherited from
boost::non_copyable - using the const auto_ptr prevents copying and
assignment (as it is not copyable/assignable). This means the class
itself becomes non copyable, as it has a non copyable member.
Admittedly, I have not looked at boost as much as I should. I'm kind of
waiting for a good book to arrive, and I want it to settle more. I'm
starting though.

> However, using the auto_ptr to hold a pimpl
> pointer is problematic because of the potential for transfer of
> ownership issues.

Transfer of ownership not possible because of constness of auto_ptr.
Also, you are right - using a raw pointer for the pimpl idiom is fine.

Regards,

Werner

werasmus

unread,
Sep 13, 2005, 8:36:43 AM9/13/05
to

kanze wrote:
> Frank Chang wrote:

> I missed earlier parts of this thread, but one thing is certain:
> you cannot use an std::auto_ptr for the compilation firewall
> idiom. It's undefined behavior to instantiation
> std::auto_ptr<T> if T is an incomplete type. (See §17.4.3.6/2.)

See my other posting - note that auto_ptr was const. I agree that I
needn't of used auto_ptr - raw one would do. As far as the incomplete
type is concerned, I don't think the type is necessarily incomplete.

class X
{
public:
~X();
private:
class Impl;
std::auto_ptr<Impl> pimpl_;
}

//Definition of Impl...
X::Impl{};

//Definition of X member functions...Is definition of X::Impl complete
// at this point? Yes, we are required to explicitly write the
// destructor
~X::X()
{
//No action required.
}

All said, I'll change my solution to:

class X : boost::non_copyable
{
//...Other
private:
class Impl;
Impl* pimpl_;
};

Frank Chang

unread,
Sep 13, 2005, 8:33:40 AM9/13/05
to
Werner, Thank you explaining the subtlety of the std::auto_ptr in the
pimpl idiom. James Kanze wrote a nice post today in reply to your
earlier post.
Regarding good books on boost, I have looked around in some
of the best technical bookstores in the world and I have found only one
book, Template Metaprogrammming. Do you know of any other books due to
be published soon? I guess the best way to learn about boost for now is
to participate in comp.lang.c++.moderated and learn from each other.
Thank you.

Frank Chang

unread,
Sep 13, 2005, 8:34:27 AM9/13/05
to
James, Thank you for explaining the use of std::auto_ptr in pimpl idiom
and the boost::scoped pointer. I read on a blog that you cannot use an
std::auto_ptr for the compilation firewall idiom, but it didn't give a
reason so I thought I that , well, transfer of ownership, is an issue
with std::auto_ptr. I will try the scoped pointer suggestion.

Tony Delroy

unread,
Sep 13, 2005, 8:34:48 AM9/13/05
to
Hi Ali,

I think it's ugly too, and I don't recall having used it, but do think
it's practical if you're lazy and more concerned about brevity of
source than performance. Might consider it if it avoided a really
repetitive constructor body while I was hacking together something for
a one-off, throw-away benchmark or proof-of-concept.

Cheers,
Tony

Ali Çehreli

unread,
Sep 14, 2005, 2:58:20 AM9/14/05
to
"werasmus" <w_e...@telkomsa.net> wrote in message
news:1126604669....@g49g2000cwa.googlegroups.com...

>
> kanze wrote:
>> Frank Chang wrote:
>
>> I missed earlier parts of this thread, but one thing is certain:
>> you cannot use an std::auto_ptr for the compilation firewall
>> idiom. It's undefined behavior to instantiation
>> std::auto_ptr<T> if T is an incomplete type. (See 17.4.3.6/2.)

I think you are referring to this text:

<quote>
if an incomplete type (3.9) is used as a template argument when
instantiating a template component.
</quote>

I think we need to take "template component" as the destructor of auto_ptr
in that context. It is when that function is "instantiated."

>
> See my other posting - note that auto_ptr was const. I agree that I
> needn't of used auto_ptr - raw one would do.

Raw one would do only in trivial cases like the example you've posted. As
soon as C's constructor becomes complicated enough to letexceptions escape,
the memory that pimpl_ points to will be leaked. As we all know, not handing
a new'ed resource immediately to a smart pointer is dangerous.

> As far as the incomplete
> type is concerned, I don't think the type is necessarily incomplete.
>
> class X
> {
> public:
> ~X();

That line is crucial.

> private:
> class Impl;
> std::auto_ptr<Impl> pimpl_;
> }
>
> //Definition of Impl...
> X::Impl{};
>
> //Definition of X member functions...Is definition of X::Impl complete
> // at this point? Yes, we are required to explicitly write the
> // destructor
> ~X::X()
> {
> //No action required.
> }

I agree with you. In fact, I had written an article on this very subject:

http://accu-usa.org/2002-02.html

I welcome any feedback...

Thank you,
Ali

--
ACCU's Silicon Valley Chapter meets on second Tuesdays. The meetings are
open to public and free of charge. Please come tonight for a talk on Ada:

http://accu-usa.org/index.html

E. Mark Ping

unread,
Sep 14, 2005, 3:00:20 AM9/14/05
to
In article <1126597715.5...@g49g2000cwa.googlegroups.com>,

kanze <ka...@gabi-soft.fr> wrote:
>I missed earlier parts of this thread, but one thing is certain:
>you cannot use an std::auto_ptr for the compilation firewall
>idiom. It's undefined behavior to instantiation
>std::auto_ptr<T> if T is an incomplete type. (See §17.4.3.6/2.)

...

>I don't think that boost::shared_ptr is appropriate either,
>unless you're trying to do some sort of copy on write. The fact
>that a class uses the compilation firewall idiom should be
>transparent to the user -- using reference semantics (shallow
>copy) rather than value semantics is anything but transparent.
>
>IMHO, boost's scoped_ptr would be the only appropriate smart
>pointer, although given the simplicity of the problem, I'm not
>sure that a smart pointer is necessary, or even appropriate.

I don't understand why auto_ptr<T> is undefined but scoped_ptr doesn't
present the same problem (granted, I've never used scoped_ptr though
I've started reading up on the boost smart ptrs).

After reading the paragraph in the standard you cited all I can find
is that the boost classes aren't in the standard. Is that the only
difference? I can't imagine the implementation being materially
different (where the functionality is similar).
--
Mark Ping
ema...@soda.CSUA.Berkeley.EDU

werasmus

unread,
Sep 14, 2005, 3:20:42 AM9/14/05
to

Ali Çehreli wrote:
> "Tony Delroy" <tony_i...@yahoo.co.uk> wrote in message
> news:1125568114.9...@o13g2000cwo.googlegroups.com...

> > Another way is:
> >
> > X::X(int a, int b) { *this = X(a + b); }
>
> Won't work.

Yes, it won't work, but not for the reason you mention. At the time you
enter the constructor, the this pointer is certainly valid, and can be
dereferenced. If at the time you dereference the this pointer, all
members have been created - as in this case, one can assign to it. One
would be able to assign to it when the constructor returns - there is
nothing more that is done wrt. allocation etc, therefore I would reason
the code here above is valid - whether good practice may be another
argument, but... it won't work because of <m> being const. This
constrains one from assigning to the object. Also <m> needs to be
initialised in the member initialiser list - something that the example
here above have omitted.

>
> The object on the left hand side of operator= is not constructed at the time
> you call the assignment. Yes, some or all of its members are default
> initialized, but the object is not.

I beg to differ - when all the members of the object is initialised,
and all that is required is the actual returning from the constructor,
then the object is as good as constructed - after all, returning from a
constructor cannot throw. If <m> was not const, and assignment was
legal at that point(IHMO), then the object construction could fail as
result of the assignment. In that case the object on the lhs's
construction will fail from an external perspective (the perpective of
the constructor callee). If I'm wrong here, someone would have to point
this to me by referring to standard. It would not seem consistent
though, as at the time of entering the constructor, all members are
valid (this includes members that require dynamic allocation if they
were i