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

enum v.s. static function in template class: which one is better ?

1 view
Skip to first unread message

Lizax

unread,
Dec 25, 2007, 5:42:19 PM12/25/07
to
I want to implement a template like this:

template<typename T, int id>
struct Foo
{
typedef T Type;
enum { value = id };
};

or

template<typename T, int id>
struct Foo
{
typedef T Type;
static int value() { return id; }
};

which one is better ?

Thanks.


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

int...@gmail.com

unread,
Dec 26, 2007, 3:19:07 PM12/26/07
to
On 26 Dec, 01:42, "Lizax" <li...@126.com> wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
>
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
>
> };
>
> which one is better ?

Enum, since it's a compile-time constant. Accessor function won't
produce one, and does not have any benefits otherwise.

Carl Barron

unread,
Dec 26, 2007, 3:18:59 PM12/26/07
to
Lizax <li...@126.com> wrote:

> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };
>
> which one is better ?
>
> Thanks.

about the sama are the enum above and

template <typename T,int Id>
struct Foo
{
typdef T Type;
static const int value = Id;
};

are better than the function version

These provide a compile time constant for the value which can be used in
compile time expressions, your function version requires runtime
evaluation.

red floyd

unread,
Dec 26, 2007, 3:18:23 PM12/26/07
to
Lizax wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };
>
> which one is better ?
>

Depends on whether you need a compile-time value or a run-time value.

ta0...@yahoo.com

unread,
Dec 26, 2007, 3:18:18 PM12/26/07
to
On Dec 26, 1:42 am, "Lizax" <li...@126.com> wrote:

> which one is better ?

That all depends on whether or not 'id' is compile-time significant.
Why not use:

template<typename T, int id>
struct Foo
{

static const int value = id;
};


There isn't much point to using a static function in this case unless
a template function expects to be able to call 'Foo::value()'.
Kevin Barry

Thomas Maeder

unread,
Dec 26, 2007, 3:19:11 PM12/26/07
to
"Lizax" <li...@126.com> writes:

> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };
>
> which one is better ?

That depends on your needs.


The first form probably has more uses, because
Foo<someT,someId>::value is a "compile time constant". This allows you
to use it in more situations than the return value of a function;
e.g. you can use it in other template instantiations.

On the other hand, you might have generic code relying on there being
a value() member function, and you want to use that code on the Foo
template as well. In that case, value() is part of the required
interface, so you have to provide it.


All other things being equal, I'd opt for the first form; it's also
the simpler one.

Anuj

unread,
Dec 26, 2007, 3:20:53 PM12/26/07
to
On Dec 26, 3:42 am, "Lizax" <li...@126.com> wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
>
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
>
> };
>
> which one is better ?
>
> Thanks.
>
> --
> [ Seehttp://www.gotw.ca/resources/clcm.htmfor info about ]

> [ comp.lang.c++.moderated. First time posters: Do this! ]

I think the first option is better, as it saves you from the function
call overhead, each time you access value.

Mathias Gaunard

unread,
Dec 26, 2007, 3:20:53 PM12/26/07
to
On 25 déc, 23:42, "Lizax" <li...@126.com> wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
>
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
>
> };
>
> which one is better ?

The first one, because it is a compile-time constant.
You might also want to use

template<typename T, int id>
struct Foo
{
typedef T Type;

static const int value = id;

};


Daniel T.

unread,
Dec 26, 2007, 3:20:57 PM12/26/07
to
"Lizax" <li...@126.com> wrote:

> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };
>
> which one is better ?

Flip a coin, or follow the uniform access principle.

alfps

unread,
Dec 26, 2007, 3:20:25 PM12/26/07
to
On Dec 25, 11:42 pm, "Lizax" <li...@126.com> wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
>
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
>
> };
>
> which one is better ?

Generally, the question "better" without some reference to the
requirements, is meaningless.

Perhaps in one context, you need to be able to use "value" at compile
time, in which case the first version would be "better". Perhaps in
some other context, you need a free function that you can pass to code
that expects a function pointer, but you don't need compile time
access to the value, in which case the second version would be
"better". Perhaps in a third context you need both abilities, in
which case a combination of the above approaches would be "better".

What are your requirements?

Michael Aaron Safyan

unread,
Dec 27, 2007, 5:08:33 AM12/27/07
to
Lizax wrote:
> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };
>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };
>
> which one is better ?
>
> Thanks.
>
>

The static function is better, in my opinion. Here's why:

1.) The enum version is often written as:

template<typename T, int id>
struct Foo
{
typedef T Type;

static const int value = id;
};


However, this version does not work on all compilers. Additionally, on
compilers which support this, taking the address of Foo<T,id>::value
will compile but cause a linker error (with GCC), unless an external
definition of the variable "int Foo<T,id>::value" is provided.

2.) The static function version works with all types, while the enum (or
static const variable) version only works with integral types. Thus,
using a static function ensures the consistency of your code, in the
event that you eventually support a similar construct with an
user-defined type.

3.) The only problem with using a static function is that the value
cannot be used in a template or other compile-time expression. However,
C++0x will introduce the keyword "constexpr", which will make the static
function version useable in compile-time expressions.

Alberto Ganesh Barbati

unread,
Dec 27, 2007, 5:11:55 AM12/27/07
to
Lizax ha scritto:

> I want to implement a template like this:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> enum { value = id };
> };

Pros: value can be used whenever a constant expression is required
Cons: value has not type int (it is "convertible to int", though)

>
> or
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static int value() { return id; }
> };

Pros: value() has type int
Cons: value() can't be used where a constant expression is required.

>
> which one is better ?
>

As you can see, each one has pros and cons. So I cannot say which one is
better than the other. However, there is another solution which is
definitely better than both:

template<typename T, int id>
struct Foo
{
typedef T Type;

static const int value = id;
};

Pros: value has type int and can be used where a constant expression is
required.
Cons: although the syntax is perfectly C++03 standard, there are a few
older compilers which don't like initializers on static const data
members. If you don't need to bother with those substandard compilers,
you don't have to worry.

Notice that only static const data members of integral type can be
initialized in the class body. If the type had not been an integral type
then you would have no choice but using a static function. In the
upcoming C++0X, the new keyword constexpr may come handy in this case:

template<typename T, int id>
struct Foo
{
typedef T Type;

static constexpr int value() { return id; }
};

By using constexpr, value() can be used as a constant expression (that's
what "constexpr" means!) so this approach is equivalent to the use of
the static const data member.

HTH,

Ganesh

Alberto Ganesh Barbati

unread,
Dec 28, 2007, 10:40:16 AM12/28/07
to
Michael Aaron Safyan ha scritto:

>
> 1.) The enum version is often written as:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static const int value = id;
> };

You may see this as a different version of the enum case, but it's not.
An enumerator is always an rvalue and has a unique type different from
int, which is convertible to int. OTOH, a static const data member is a
const lvalue and has type int. This may make a lot of difference.

> However, this version does not work on all compilers.

True. Those compilers are non-conforming, though.

> Additionally, on
> compilers which support this, taking the address of Foo<T,id>::value
> will compile but cause a linker error (with GCC), unless an external
> definition of the variable "int Foo<T,id>::value" is provided.

You can also see this the other way round. As a static const data member
is an lvalue, you *can* take its address as long as you also provide a
definition of the member. OTOH, you can *never* take the address of an
enumerator or a value returned by a static function, no matter what,
because they are rvalues.

> 3.) The only problem with using a static function is that the value
> cannot be used in a template or other compile-time expression. However,

This may be the *only* problem, but it's a big one!

> C++0x will introduce the keyword "constexpr", which will make the static
> function version useable in compile-time expressions.

Yup, constexpr is most welcome to address this issue.

HTH,

Ganesh

alfps

unread,
Dec 28, 2007, 2:46:34 PM12/28/07
to
On Dec 27, 11:11 am, Alberto Ganesh Barbati <AlbertoBarb...@libero.it>
wrote:

> Lizax ha scritto:
>
> > I want to implement a template like this:
>
> > template<typename T, int id>
> > struct Foo
> > {
> > typedef T Type;
> > enum { value = id };
> > };
>
> Pros: value can be used whenever a constant expression is required
> Cons: value has not type int (it is "convertible to int", though)
>
> > or
>
> > template<typename T, int id>
> > struct Foo
> > {
> > typedef T Type;
> > static int value() { return id; }
> > };
>
> Pros: value() has type int
> Cons: value() can't be used where a constant expression is required.

OK.


> > which one is better ?
>
> As you can see, each one has pros and cons. So I cannot say which one is
> better than the other. However, there is another solution which is
> definitely better than both:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static const int value = id;
>
> };

This is definitely worse, in general, because (1) if the "value" is
used in a way that requires it to have an address, a separate
definition is required (the above is only a declaration, even though
it looks like a definition), and (2) although formally standard C++,
different compilers vary in their support for the construct, including
accepting invalid code and not accepting valid code.


> Pros: value has type int and can be used where a constant expression is
> required.
> Cons: although the syntax is perfectly C++03 standard, there are a few
> older compilers which don't like initializers on static const data
> members. If you don't need to bother with those substandard compilers,
> you don't have to worry.

This problem is not restricted to older compilers, and it's not
restricted to accepting initializers on static const data.

> Notice that only static const data members of integral type can be
> initialized in the class body.

Per the current standard. Different compilers vary in what they
accept.


> If the type had not been an integral type
> then you would have no choice but using a static function.

I'm sorry, that's incorrect. True, by the current standard non-
integral type static constant can't be initialized in the class
definition. False, that this precludes having the non-integral value
as a typed static constant.

It would require a separate definition, in addition to the in-class
declaration (this is also true for the integral constant, if its
address is ever required).

And the value would then have to be supplied in the separate
definition.


> In the
> upcoming C++0X, the new keyword constexpr may come handy in this case:
>
> template<typename T, int id>
> struct Foo
> {
> typedef T Type;
> static constexpr int value() { return id; }
>
> };
>
> By using constexpr, value() can be used as a constant expression (that's
> what "constexpr" means!) so this approach is equivalent to the use of
> the static const data member.

With current compilers (10 years after standardization) lacking full
support for, among other things, class definition initialization of
integral static constants, one may reasonably ask how many more years
to wait before "constexpr" will be truly portable... :-)

Anyways, summing up, if you want a portable compile time integral
constant with initialization in the class definition, the way to go is
"enum".

However, the OP neglected to state his/her requirements, so what's
"better" for the OP's intended application is an open question.


Cheers, & hth.,

- Alf (noting in passing that the only response so far in this thread
with incorrect information, was the only one acquiring stars -- 5 of
them -- in Google Groups).

Alberto Ganesh Barbati

unread,
Dec 29, 2007, 3:34:31 PM12/29/07
to
alfps ha scritto:

> On Dec 27, 11:11 am, Alberto Ganesh Barbati <AlbertoBarb...@libero.it>
> wrote:
>> Lizax ha scritto:
>>> which one is better ?
>> As you can see, each one has pros and cons. So I cannot say which one is
>> better than the other. However, there is another solution which is
>> definitely better than both:
>>
>> template<typename T, int id>
>> struct Foo
>> {
>> typedef T Type;
>> static const int value = id;
>>
>> };
>
> This is definitely worse, in general, because (1) if the "value" is
> used in a way that requires it to have an address, a separate
> definition is required (the above is only a declaration, even though
> it looks like a definition), and (2) although formally standard C++,
> different compilers vary in their support for the construct, including
> accepting invalid code and not accepting valid code.

(1) is a not a big deal, IMHO. About (2) I already acknowledged in my
post that compatibility problems are an issue. Given that we weren't
given precise requirements, whether these two facts make static const
worse than the other options is a matter of opinion.

>> Pros: value has type int and can be used where a constant expression is
>> required.
>> Cons: although the syntax is perfectly C++03 standard, there are a few
>> older compilers which don't like initializers on static const data
>> members. If you don't need to bother with those substandard compilers,
>> you don't have to worry.
>
> This problem is not restricted to older compilers, and it's not
> restricted to accepting initializers on static const data.

Any such compiler is non-conforming anyway.

>> Notice that only static const data members of integral type can be
>> initialized in the class body.
>
> Per the current standard. Different compilers vary in what they
> accept.

Per the current standard. Yes.

>> If the type had not been an integral type
>> then you would have no choice but using a static function.
>
> I'm sorry, that's incorrect. True, by the current standard non-
> integral type static constant can't be initialized in the class
> definition. False, that this precludes having the non-integral value
> as a typed static constant.
>
> It would require a separate definition, in addition to the in-class
> declaration (this is also true for the integral constant, if its
> address is ever required).
>
> And the value would then have to be supplied in the separate
> definition.

You are correct, but in that case the value could not be used in
constant expressions and every use of it would need to be evaluated at
run-time rather then at compile time, losing the possibly to trigger
optimizations. This is a completely different case, which I did not care
mentioning because the comparison with the enum and static function was
not fair.

>> In the
>> upcoming C++0X, the new keyword constexpr may come handy in this case:
>>
>> template<typename T, int id>
>> struct Foo
>> {
>> typedef T Type;
>> static constexpr int value() { return id; }
>>
>> };
>>
>> By using constexpr, value() can be used as a constant expression (that's
>> what "constexpr" means!) so this approach is equivalent to the use of
>> the static const data member.
>
> With current compilers (10 years after standardization) lacking full
> support for, among other things, class definition initialization of
> integral static constants, one may reasonably ask how many more years
> to wait before "constexpr" will be truly portable... :-)

Who knows? Maybe this time compilers will catch up more quickly... Don't
be so negative! ;-)

> Anyways, summing up, if you want a portable compile time integral
> constant with initialization in the class definition, the way to go is
> "enum".

If portability with non-conforming compilers is an issue, then I agree
with you. In all other cases, I stand my opinion.

> - Alf (noting in passing that the only response so far in this thread
> with incorrect information, was the only one acquiring stars -- 5 of
> them -- in Google Groups).

I resent that you consider my post to contain incorrect information,
because it didn't. Anyway I didn't vote for my post, if you are
suggesting that.

Ganesh

Yechezkel Mett

unread,
Dec 31, 2007, 2:08:32 AM12/31/07
to
On Dec 28, 5:40 pm, Alberto Ganesh Barbati <AlbertoBarb...@libero.it>
wrote:

> Michael Aaron Safyan ha scritto:
> > 1.) The enum version is often written as:
>
> > template<typename T, int id>
> > struct Foo
> > {
> > typedef T Type;
> > static const int value = id;
> > };
...

> > Additionally, on
> > compilers which support this, taking the address of Foo<T,id>::value
> > will compile but cause a linker error (with GCC), unless an external
> > definition of the variable "int Foo<T,id>::value" is provided.
>
> You can also see this the other way round. As a static const data member
> is an lvalue, you *can* take its address as long as you also provide a
> definition of the member. OTOH, you can *never* take the address of an
> enumerator or a value returned by a static function, no matter what,
> because they are rvalues.

What if the value is passed by const reference? If I understand
correctly, with an enum or a static function it will work, copying the
value, but with a static variable the linker will complain if there is
no external definition. (True, ints aren't normally passed by const
reference, but it might happen in a template.)

Yechezkel Mett

Alberto Ganesh Barbati

unread,
Jan 3, 2008, 6:13:13 PM1/3/08
to
Yechezkel Mett ha scritto:

> On Dec 28, 5:40 pm, Alberto Ganesh Barbati <AlbertoBarb...@libero.it>
> wrote:
>> Michael Aaron Safyan ha scritto:
>>> 1.) The enum version is often written as:
>>> template<typename T, int id>
>>> struct Foo
>>> {
>>> typedef T Type;
>>> static const int value = id;
>>> };
> ...
>>> Additionally, on
>>> compilers which support this, taking the address of Foo<T,id>::value
>>> will compile but cause a linker error (with GCC), unless an external
>>> definition of the variable "int Foo<T,id>::value" is provided.
>> You can also see this the other way round. As a static const data member
>> is an lvalue, you *can* take its address as long as you also provide a
>> definition of the member. OTOH, you can *never* take the address of an
>> enumerator or a value returned by a static function, no matter what,
>> because they are rvalues.
>
> What if the value is passed by const reference? If I understand
> correctly, with an enum or a static function it will work, copying the
> value, but with a static variable the linker will complain if there is
> no external definition. (True, ints aren't normally passed by const
> reference, but it might happen in a template.)
>

The standard says that "The member shall still be defined in a namespace
scope if it is used in the program" (9.4.2/4). Unfortunately what
constitutes a "use" is not specified as clearly. Anyway, I believe you
understand correctly in the sense that passing the static member by
(const) reference should constitute a "use" and therefore should require
a definition.

Despite the obvious annoyance of requiring the definition, there is an
interesting thing to notice, consider this code:

struct S
{
enum { a = 0; }
static int b() { return 0; }
static const int c = 0;
};

void f(const int&);

f(S::a); // (1)
f(S::b()); // (2)
f(S::c); // (3)

Assuming f() is not inlined, in both cases (1) and (2) a temporary will
be created, initialized and its address passed to f(). However, in (3)
the address of S::c is just passed to f(). I don't expect the difference
to be significant, yet it might have some impact on specific
architectures. It's not at all obvious which approach is better. For
example, reading S::c might result in a cache miss, but there's one less
memory write. Just food for thought.

Ganesh

0 new messages