7

Say I have the following example:

#include <cstdlib>

class A {
public:
    static const std::size_t value = 42;
};

In short, I have (or better, want) a class A with a static const std::size_t member called value with the value 42 (determined at compile time).

Now, IIRC, this only works fine under certain circumstances. It doesn't, for example, when you take the address of A::value. In order for this to work fine in all cases you'd need to add a definition in some implementation file:

const std::size_t A::value;

However, I can't do this, because I want this file to be header-only. Another common solution is this:

class A {
public:
    enum { value = 42 };
};

I don't like this solution either, because I'd like the type of A::value to be std::size_t.

What is a good solution to this problem? Preferably a small and portable solution, not something with huge macro magic like BOOST_STATIC_CONSTANT.


I'd like a solution for C++03, not C++11 (it's trivial there).

orlp
  • 112,504
  • 36
  • 218
  • 315

1 Answers1

8

First of all, using the unsigned size_t type for numbers, you're likely to run into implicit promotion problems. So, good idea to use its corresponding signed type, which is called ptrdiff_t. Which, as it happens, is the result type of a pointer difference expression.

Also, due to changes in C++11, it’s generally a good idea to include <stddef.h> and not <cstddef>, i.e., write ::ptrdiff_t or just plain ptrdiff_t, not std::ptrdiff_t.

Now, here's how to do the header file extern linkage constant thing:

template< class Dummy >
struct A_constants_
{
    static ::ptrdiff_t const value;
};

template< class Dummy >
::ptrdiff_t const A_constants_<Dummy>::value = 42;

typedef A_constants_<void> A_constants;

class A
    : public A_constants
{
public:
    // Whatever
};

Then you can use it like this:

foo( A::value );

There are also some other ways of doing this, but the above is about the simplest and easiest to get right.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Would you mind explaining why this works? And does this mean that each compilation unit including this gets it's own `A::value` (with a unique address)? – orlp Sep 11 '12 at 15:46
  • @nightcracker: it works because the C++ standard's ODR (One Definition Rule) has a special exemption for constants in template classes, because the only practical way to provide C++ template code is in header files. So this means that the address of the constant is guaranteed the same in all translation units, if that address is accessed. However, compilers are, as always, free to optimize under the "as if" rule, which means that there's no guarantee that the value won't be duplicated, using up "needless" space. Except that as far as I can see it would it take an unusual compiler to do that. – Cheers and hth. - Alf Sep 13 '12 at 18:51
  • 1
    @Cheersandhth.-Alf i know its late but could you explain **Also, due to changes in C++11, it’s generally a good idea to include and not , i.e., write ::ptrdiff_t or just plain ptrdiff_t, not std::ptrdiff_t** ? perhaps as an edit or in you blog ? thanks. – Koushik Shetty Dec 23 '13 at 08:25
  • 1
    @Koushik: Someone commented on **[an old answer of mine about it](http://stackoverflow.com/questions/10625716/in-what-cases-we-need-to-include-cassert/10626042#10626042)** yesterday, so, herewith. :-) – Cheers and hth. - Alf Dec 23 '13 at 19:39