Named Constructor idiom - a question

S

Steve

Hi,

I thought I had invented a pretty neat solution for my problem, but it turns
out I'm using the named constructor idiom. :)

I've seen examples where empty structs are used to identify the different
constructors by their first parameter:

struct Type1 {};
struct Type2 {};

class Foo
{
public:
Foo( Type1, int x );
Foo( Type2, int y );
};

Foo a = Foo( Type1(), 10 );
Foo b = Foo( Type2(), 10 );

My solution was to use enums to differentiate them:

enum Type1 { eType1 };
enum Type2 { eType2 };

class Foo
{
public:
Foo( Type1, int x );
Foo( Type2, int y );
};

Foo a = Foo( eType1, 10 );
Foo b = Foo( eType2, 10 );


Are there any advantages or disadvantages to either method?


I guess the enums take up storage space, but so do the empty structs right?

Enums need 2 identifiers - the type name and the value, whereas the struct
only needs one identifier?

Enums don't need a () to construct them.


I'm not convinced either way (yet!). Indeed, is another method?

Does anyone have any views?
 
V

Victor Bazarov

Steve said:
I thought I had invented a pretty neat solution for my problem, but it turns
out I'm using the named constructor idiom. :)

I've seen examples where empty structs are used to identify the different
constructors by their first parameter:

struct Type1 {};
struct Type2 {};

class Foo
{
public:
Foo( Type1, int x );
Foo( Type2, int y );
};

Foo a = Foo( Type1(), 10 );
Foo b = Foo( Type2(), 10 );

Why not simply

Foo a(Type1(), 10);
Foo b(Type2(), 10);

???
My solution was to use enums to differentiate them:

enum Type1 { eType1 };
enum Type2 { eType2 };

class Foo
{
public:
Foo( Type1, int x );
Foo( Type2, int y );
};

Foo a = Foo( eType1, 10 );
Foo b = Foo( eType2, 10 );

Again, you don't need the '='...
Are there any advantages or disadvantages to either method?

No difference, AFAICS.
I guess the enums take up storage space, but so do the empty structs right?
Right.

Enums need 2 identifiers - the type name and the value, whereas the struct
only needs one identifier?

No, they don't. You can use the enums like you did structs:

Foo a(Type1(), 10);

Since enum Type1 is a separate type, and you're not using the actual value
of it in your constructor, Type1() is just constructing an temporary of
the enum Type1 type, and gives it value 0.
Enums don't need a () to construct them.

Unless you use the enumeration (type) name.
I'm not convinced either way (yet!). Indeed, is another method?

Does anyone have any views?

You can make a template constructors:

class Type1 {};
class Type2 {};

class Foo {
public:
template<class T> Foo(T,int) {
// implement the default behaviour
}
};

template<> Foo::Foo<Type1>(Type1, int i) {
// implement the first one
}

template<> Foo::Foo<Type2>(Type2, int i) {
// implement the other one
}

int main() {
Foo a(Type1(), 10);
Foo b(Type2(), 10);
Foo ab(int(), 10);
}

I am not sure what the advantage might be... The only thing I can
think of at the moment is that if you construct from some third type,
you will end up using the "default" specialisation, from the class
definition... Not much, I guess :)

V
 
S

Steve

Why not simply

Foo a(Type1(), 10);
Foo b(Type2(), 10);

???

Ummm, yes, errr... well spotted... ;-)
No, they don't. You can use the enums like you did structs:

Foo a(Type1(), 10);

Since enum Type1 is a separate type, and you're not using the actual value
of it in your constructor, Type1() is just constructing an temporary of
the enum Type1 type, and gives it value 0.


Unless you use the enumeration (type) name.

[sound of light switch...] Ah, yes, of course - just like int(), etc.
You can make a template constructors:
[snipped example code]

I am not sure what the advantage might be... The only thing I can
think of at the moment is that if you construct from some third type,
you will end up using the "default" specialisation, from the class
definition... Not much, I guess :)

I would say that¹s less safe, since I want to make sure only those
constructors I specify can be used.


Thanks very much your input, Victor. At least I know now I'm not going
wildly off track!
 
M

Mogens Heller Jensen

I guess the enums take up storage space, but so do the empty structs
right?

Actually I don't think either solution will take up any space at all.
Definitions of data types (structs, enums) don't take up any space, as this
information is only used at compile time.

You don't allocate anything, except temporary instances of empty structs,
which take up as much space as an ordinary pointer = 4 bytes on 32 bit
systems.

Using your enum solution will just convert the enums to integer constants
which also take up 4 bytes on a 32 bit system - again, the type information
is only used at compile time to link to the right function.

Either way you get what you want, and everything is resolved at compile time
so it doesn't take up any runtime resources :eek:)

The best,
Mogens
 
S

Steve

Actually I don't think either solution will take up any space at all.
Definitions of data types (structs, enums) don't take up any space, as this
information is only used at compile time.

You don't allocate anything, except temporary instances of empty structs,
which take up as much space as an ordinary pointer = 4 bytes on 32 bit
systems.

Using your enum solution will just convert the enums to integer constants
which also take up 4 bytes on a 32 bit system - again, the type information
is only used at compile time to link to the right function.

Either way you get what you want, and everything is resolved at compile time
so it doesn't take up any runtime resources :eek:)


You've just contradicted yourself?! First you say they don't take up space,
then you say they use 4 bytes?

There must be storage *somewhere* because I could take the address of struct
or enum?
 
M

Matthias Kaeppler

Steve said:
I'm not convinced either way (yet!). Indeed, is another method?

Yes, the one you mention in your headline but not in your post:
A named constructor. What you posted is not a named constructor but a
normal constructor feeded with a tag-type. I think a better, less
confusing approach would be this:

class Foo {
private:
Foo(int x) { /* ... */ }
public:
static Foo type1(int x) {
/* construct and return type1 Foo */
}
static Foo type2(int x) {
/* construct and return type2 Foo */
}
};

See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8 for a more
comprehensive example of how to use this idiom.
 
S

Steve

Yes, the one you mention in your headline but not in your post:
A named constructor. What you posted is not a named constructor but a
normal constructor feeded with a tag-type. I think a better, less
confusing approach would be this:

class Foo {
private:
Foo(int x) { /* ... */ }
public:
static Foo type1(int x) {
/* construct and return type1 Foo */
}
static Foo type2(int x) {
/* construct and return type2 Foo */
}
};

See http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.8 for a more
comprehensive example of how to use this idiom.


I did consider that, but the objects I'm really constructing in my
application are non-copyable. (I posted a simplified example!).
 
M

Matthias Kaeppler

Steve said:
I did consider that, but the objects I'm really constructing in my
application are non-copyable. (I posted a simplified example!).

So they're non-copyable (must have missed that). And how does that
relate to the named ctor idiom? No copy construction is involved, just
usual constructor calls.
 
A

Alf P. Steinbach

* Matthias Kaeppler:
So they're non-copyable (must have missed that). And how does that
relate to the named ctor idiom? No copy construction is involved, just
usual constructor calls.

In order to directly be a function result type the type needs to be
copyable, even if the copy constructor call is optimized away in practice
(as it usually is). If the type is not copyable dynamic allocation is
required. So it's a question of efficiency and convenience.
 
M

Mogens Heller Jensen

Steve said:
You've just contradicted yourself?! First you say they don't take up
space,
then you say they use 4 bytes?

There must be storage *somewhere* because I could take the address of
struct
or enum?

Oh yeah, I seem to have had some trouble explaining myself... what I meant
was that _definitions of datatypes_, be it classes, structs or enums, do not
take up any space, as they are merely information for the compiler to use
during compilation.

If you instantiate something, it will of course take up some space - so if
you instantiate e.g. a struct (by passing it as a temporary), of course
there will be a pointer somewhere.

But in this case it will not take up any more space than the pointer itself,
since the struct is empty, which amounts to 2 x sizeof(structpointer)

Enums are just treated as constants by the compiler, so again: it will take
up space if you instantiate them. So if no instances exist, the only space
used will be the constants deducted from them - in this case 2 x
sizeof(int).

Hope this clears it.

The best,
Mogens
 
M

Matthias Kaeppler

Alf said:
In order to directly be a function result type the type needs to be
copyable, even if the copy constructor call is optimized away in practice
(as it usually is). If the type is not copyable dynamic allocation is
required. So it's a question of efficiency and convenience.

Point taken.
 

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,981
Messages
2,570,187
Members
46,730
Latest member
AudryNolan

Latest Threads

Top