How to add and call a do-nothing non-default constructor?

N

Ng Hun Yang

I have a class that has a default constructor that initializes the
class's variables. Now I want to add a new constructor to skip the
initialization.

My attempt:
class Integer {
public:
class NoInit { }; // newly added

public:
Integer(): v_(0) { }
Integer(int v): v_(v) { }
Integer(NoInit) { } // newly added

private:
int v_;
};

Here's how it's used:
class SomeClass {
public:
SomeClass() { }

private:
Integer a_;
};

I want SomeClass's constructor to skip initializing a_, so I changed it
to:
SomeClass(): a_(Integer(Integer::NoInit())) { }

Shouldn't the compiler do RVO, find that it's an empty object and
optimize the initialization away?

But I found that the compiler insisted on copying from the
uninitialized temporary variable to a_.

I read about using dummy classes/enum/types as constructor parameters
to distinguish between constructors, but I can't recall where I read
it.

What did I do wrong and what should I change to achieve my goal?

I can't change Integer's default constructor, because there is code
that depends on the value being initialized. However, SomeClass is
special because it never uses the default value.

Here's how SomeClass looks like:
class SomeClass {
public:
SomeClass() { }

public:
void Start(const Integer &a) { a_ = a; fn1(); fn2(); fn3(); }

private:
void fn1(); // does something with a_
void fn2(); // does something with a_
void fn3(); // does something with a_

private:
Integer a_;
};

Why not change SomeClass to:
class SomeClass {
public:
SomeClass(const Integer &a): a_(a) { }

public:
void Start() { fn1(); fn2(); fn3(); }
};

Well, that's not the question I'm asking, isn't it? :-D


btw, I'm using the Green Hill's C++ MIPS compiler; optimization is
turned on. The Multi suite is version 4.something, but I don't know the
actual compiler version.
 
V

Victor Bazarov

Ng said:
I have a class that has a default constructor that initializes the
class's variables. Now I want to add a new constructor to skip the
initialization.

My attempt:
class Integer {
public:
class NoInit { }; // newly added

public:
Integer(): v_(0) { }
Integer(int v): v_(v) { }
Integer(NoInit) { } // newly added

private:
int v_;
};

Here's how it's used:
class SomeClass {
public:
SomeClass() { }

private:
Integer a_;
};

I want SomeClass's constructor to skip initializing a_, so I changed it
to:
SomeClass(): a_(Integer(Integer::NoInit())) { }

Why the unnecessary intermediary? Couldn't you just write

SomeClass() : a_(Integer::NoInit()) {}

?
Shouldn't the compiler do RVO, find that it's an empty object and
optimize the initialization away?

No, it should just use a particular constructor, which will leave the
'v_' data member uninitialised.
But I found that the compiler insisted on copying from the
uninitialized temporary variable to a_.

Yes, you wrote it that way.
I read about using dummy classes/enum/types as constructor parameters
to distinguish between constructors, but I can't recall where I read
it.

What did I do wrong and what should I change to achieve my goal?

Remove the extra temporary from the initialisation.

V
 
N

Ng Hun Yang

Why the unnecessary intermediary? Couldn't you just write
SomeClass() : a_(Integer::NoInit()) {}
?

hmm, you're right! I forget a_(...) itself is a constructor call!

SomeClass() : a_(Integer::NoInit()) {}

works!

(Looks like I have to remove C++ from my resume. ;-))

But suppose now I convert this to the named constructor idiom:
class Integer {
private:
enum NoInitT { NoInitV };
explicit Integer(NoInitT) { }

public:
Integer(): v_(0) { }
Integer(int v): v_(v) { }
static Integer NoInit() { return Integer(NoInitV); }

private:
int v_;
};

class SomeClass {
public:
SomeClass(): a_(Integer::NoInit()) { }

private:
Integer a_;
};

Still the same thing, right?

However, NoInit() creates a temporary (uninitialized) return object
which is then copied to a_. Can't the compiler initialize a_ in-place?
 
V

Victor Bazarov

Ng said:
hmm, you're right! I forget a_(...) itself is a constructor call!

SomeClass() : a_(Integer::NoInit()) {}

works!

(Looks like I have to remove C++ from my resume. ;-))

If folks did that every time they use an extra copy, we'd probably have
less than a hundred of people with C++ on their resumes. :)
But suppose now I convert this to the named constructor idiom:
class Integer {
private:
enum NoInitT { NoInitV };
explicit Integer(NoInitT) { }

public:
Integer(): v_(0) { }
Integer(int v): v_(v) { }
static Integer NoInit() { return Integer(NoInitV); }

private:
int v_;
};

class SomeClass {
public:
SomeClass(): a_(Integer::NoInit()) { }

private:
Integer a_;
};

Still the same thing, right?

However, NoInit() creates a temporary (uninitialized) return object
which is then copied to a_. Can't the compiler initialize a_ in-place?

I might be able to, or it might not. There is no requirement in the
Standard that the compiler does so. The Standard allows, however, to
omit the copying and initialise an object directly, even if the copy
constructor has side effects. So, set your optimization levels high
and hope for the best :)

V
 

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,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top