A constructor calling another constructor (default constructor)?

  • Thread starter Generic Usenet Account
  • Start date
G

Generic Usenet Account

I am having an argument with a colleague who insists that an
overloaded constructor can call the default constructor using the
initializer list syntax ---- something like this:

class SomeClass
{
public:
SomeClass()
{
std::cout << "This is the default constructor\n";
}

SomeClass(const string& str):SomeClass()
{
std::cout << "This is the overloaded constructor\n";
}

};

I am convinced that is not possible ---- and our compiler also does
not support it. Can someone point me to the correct portion of the C+
+ standard where I can find documentary proof to the contrary? I am
finding 597 references to the word "constructor" in the standard :-(

Thanks,
Song
 
V

Victor Bazarov

Generic said:
I am having an argument with a colleague who insists that an
overloaded constructor can call the default constructor using the
initializer list syntax ---- something like this:

class SomeClass
{
public:
SomeClass()
{
std::cout << "This is the default constructor\n";
}

SomeClass(const string& str):SomeClass()
{
std::cout << "This is the overloaded constructor\n";
}

};

I am convinced that is not possible ---- and our compiler also does
not support it. Can someone point me to the correct portion of the C+
+ standard where I can find documentary proof to the contrary? I am
finding 597 references to the word "constructor" in the standard :-(

In the current Standard it's not possible because "constructors do not
have names and cannot be called". But in the future edition I believe
there is a change that allows forwarding the control to different
constructors, like you showed here. I'm too lazy to look up the doc
number for that proposal.

See [class.ctor]/2.

In the proposed standard draft (n2315) I see this ([class.base.init]/2)
<<
A mem-initializer-list can delegate to another constructor of the
constructor's class using any name that denotes the constructor's
class itself. If a mem-initializer-id designates the constructor's
class, it shall be the only mem-initializer; the constructor is
a delegating constructor, and the constructor selected by the mem-
initializer is the target constructor. The principal constructor is
the first constructor invoked in the construction of an object
(that is, not a target constructor for that object's construction).
The target constructor is selected by overload resolution. Once the
target constructor returns, the body of the delegating constructor
is executed. If a constructor delegates to itself directly or
indirectly, the program is ill-formed; no diagnostic is required.
[ Example:
struct C {
C( int ) { } // 1: non-delegating constructor
C(): C(42) { } // 2: delegates to 1
C( char c ) : C(42.0) { } // 3: ill-formed due to recursion with 4
C( double d ) : C('a') { } // 4: ill-formed due to recursion with 3
};
- end example]
<<

HTH

V
 
A

Alf P. Steinbach

* Victor Bazarov:
As Victor notes below it's part of C++0x but not of the current standard.

For the current C++0x draft go to the committee pages, <url:
http://www.open-std.org/jtc1/sc22/wg21/docs/projects#14882>.

Keeping within the current language, you can use either an init member
function (practically ugly and limited and unsafe) or derive from an
articial base class (practically much better and general but offensive
to design purists -- it's not mentioned in the FAQ).

In the current Standard it's not possible because "constructors do not
have names and cannot be called".

Do not have names and so are not found during function name lookup.

An overload set is formed nonetheless, and overload resolution performed
for the call.

But in the future edition I believe
there is a change that allows forwarding the control to different
constructors, like you showed here. I'm too lazy to look up the doc
number for that proposal.

See [class.ctor]/2.

In the proposed standard draft (n2315) I see this ([class.base.init]/2)
<<
A mem-initializer-list can delegate to another constructor of the
constructor's class using any name that denotes the constructor's
class itself. If a mem-initializer-id designates the constructor's
class, it shall be the only mem-initializer; the constructor is
a delegating constructor, and the constructor selected by the mem-
initializer is the target constructor. The principal constructor is
the first constructor invoked in the construction of an object
(that is, not a target constructor for that object's construction).
The target constructor is selected by overload resolution. Once the
target constructor returns, the body of the delegating constructor
is executed. If a constructor delegates to itself directly or
indirectly, the program is ill-formed; no diagnostic is required.
[ Example:
struct C {
C( int ) { } // 1: non-delegating constructor
C(): C(42) { } // 2: delegates to 1
C( char c ) : C(42.0) { } // 3: ill-formed due to recursion with 4
C( double d ) : C('a') { } // 4: ill-formed due to recursion with 3
};
- end example]
<<

Cheers,

- Alf
 
R

robertwessel2

In the current Standard it's not possible because "constructors do not
have names and cannot be called". But in the future edition I believe
there is a change that allows forwarding the control to different
constructors, like you showed here. I'm too lazy to look up the doc
number for that proposal.


You can hack it with placement new. Ugly but it works. And I can't
really imagine why you wouldn't just move the default ctor's code into
a common routine you could call from the others.


#include <iostream>

class SomeClass
{
public:
SomeClass()
{
std::cout << "This is the default constructor\n";
}

SomeClass(int i)
{
std::cout << "This is the int constructor\n";
}

SomeClass(double d)
{
std::cout << "This is the double constructor\n";
new(this) SomeClass; // Invoke default ctor
}

void * operator new(size_t, void *p) {return p;}

};



int main(void)
{
SomeClass sc;
SomeClass sci(1);
SomeClass scd(2.0);

return 0;
}
 
A

Alf P. Steinbach

* (e-mail address removed):
You can hack it with placement new. Ugly but it works.

Apart from the UB it imposes impractical requirements on derived classes.

And I can't
really imagine why you wouldn't just move the default ctor's code into
a common routine you could call from the others.

One case is where a base class sub-object has no default constructor.

Cheers, & hth.,

- Alf
 
K

Kaz Kylheku

I am having an argument with a colleague who insists that an
overloaded constructor can call the default constructor using the
initializer list syntax ---- something like this:

class SomeClass
{
public:
SomeClass()
{
std::cout << "This is the default constructor\n";
}

SomeClass(const string& str):SomeClass()

This is not well formed, because the ``mem-initializer-id'' names
something which is not a member of the class, nor is a direct or
indirect base class.
not support it. Can someone point me to the correct portion of the C+
+ standard where I can find documentary proof to the contrary?

12.6.2. This is where the ctor-initializer syntax is presented along
with its semantics.

Paragraph 2 introduces the explicit constraints on the names in a
member initializer.
 
K

Kaz Kylheku

I am having an argument with a colleague who insists that an
overloaded constructor can call the default constructor using the
initializer list syntax ---- something like this:

class SomeClass
{
public:
SomeClass()
{
std::cout << "This is the default constructor\n";
}

SomeClass(const string& str):SomeClass()

This is not well formed, because the ``mem-initializer-id'' names
something which is not a nonstatic data member of the class, nor is a
direct or virtual base of SomeClass.
not support it. Can someone point me to the correct portion of the C+
+ standard where I can find documentary proof to the contrary?

12.6.2. This is where the ctor-initializer syntax is presented along
with its semantics.

Paragraph 2 introduces the explicit constraints on the names in a
member initializer.
 
R

robertwessel2

* (e-mail address removed):



Apart from the UB it imposes impractical requirements on derived classes.


What UB? I'm not saying there isn't, but I'm not seeing anything that
would forbid this.
 
J

Juha Nieminen

And I can't
really imagine why you wouldn't just move the default ctor's code into
a common routine you could call from the others.

Because there may be member variables which cannot be initialized in a
member function, only in the initializer list of a constructor?
 
A

Alf P. Steinbach

* (e-mail address removed):
What UB? I'm not saying there isn't, but I'm not seeing anything that
would forbid this.

UB appears when there is a base or member with non-trivial destructor
/and/ the program "depends on the side effects produced by the
destructor" per §3.8/4. Essentially because the standard guarantees
that the destructor isn't called implicitly, so it isn't called. And
since it's impossible to know about dependencies on side effects of
destructors of objects of library classes, it's just plain general UB.

Of course the fix for that UB is to introduce an explicit destructor
call. Which also gets rid of possible memory leaks (e.g. from a
std::string member whose destructor would otherwise not be called).

But then there is a more subtle UB argument which would apply more
generally (even for the case of an empty class such as the example code)
namely that by completing execution of two constructors of class T for
the same memory region, two objects have been created in the same memory
region. And the argument has been put forward that this is illegal for
non-POD classes, but I haven't seen this backed up by references to the
standard. On the other hand, it's a practically important argument,
because constructor calls are special and typically set up vtables and
may have to keep track of where they're called from (in the case of
virtual inheritance), and reconstructing the object under construction,
even when remembering to first destroy it by using an explicit
destructor call, very likely invalidates the compiler's assumptions.

Cheers, & hth.,

- Alf
 
R

robertwessel2

But then there is a more subtle UB argument which would apply more
generally (even for the case of an empty class such as the example code)
namely that by completing execution of two constructors of class T for
the same memory region, two objects have been created in the same memory
region. And the argument has been put forward that this is illegal for
non-POD classes, but I haven't seen this backed up by references to the
standard.


I also started thinking about the non-POD member case after I wrote
that. At the very least running the placement new hack would run the
member constructors twice, which might be harmless in some cases, but
certainly has the potential for odd results in others.
 

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,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top