24

Consider this example code:

template<class D>
char register_(){
    return D::get_dummy(); // static function
}

template<class D>
struct Foo{
    static char const dummy;
};

template<class D>
char const Foo<D>::dummy = register_<D>();

struct Bar
    : Foo<Bar>
{
    static char const get_dummy() { return 42; }
};

(Also on Ideone.)

I'd expect dummy to get initialized as soon as there is a concrete instantiation of Foo, which I have with Bar. This question (and the standard quote at the end) explained pretty clear, why that's not happening.

[...] in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.

Is there any way to force dummy to be initialized (effectively calling register_) without any instance of Bar or Foo (no instances, so no constructor trickery) and without the user of Foo needing to explicitly state the member in some way? Extra cookies for not needing the derived class to do anything.


Edit: Found a way with minimal impact on the derived class:

struct Bar
    : Foo<Bar>
{   //                              vvvvvvvvvvvv
    static char const get_dummy() { (void)dummy; return 42; }
};

Though, I'd still like the derived class not having to do that. :|

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • We generally don't want the compiler to initialize all unused variables in all class templates. How should the compiler know that **in this case** you want it to? By actually using it? – Bo Persson Jun 21 '11 at 06:34
  • 1
    @Bo: Sure, but I'd like to hide that use from the derived class / outer world and would rather somehow get that into `Foo` itself. :/ – Xeo Jun 21 '11 at 06:35
  • @Xeo: `static char const get_dummy() { (void)dummy; return 42; }` - I doubt that this circular dependency between `Bar::get_dummy()` and `Foo::dummy` is guaranteed to work (by the standard). Looks like highly implementation-dependent trick. Am I wrong? – Serge Dundich Jun 21 '11 at 06:47
  • 1
    @Serge: Why shouldn't it? The `dummy` in `get_dummy` is the uninitialized one, sure, but that doesn't matter. :) I don't actually use it anyways. – Xeo Jun 21 '11 at 10:06
  • 1
    @Xeo: "The dummy in get_dummy is the uninitialized one, sure, but that doesn't matter. :) I don't actually use it anyways." Yea. But why it is supposed to force `Foo::dummy` to become initialized? And what is supposed to prevent compiler from optimizing statement `(void)dummy;` (that has no effect) out. – Serge Dundich Jun 21 '11 at 10:21
  • 1
    @Serge: It forces `dummy` to become initialized, because it is *used*, as quoted. w.r.t. optimizations, I'll have to check that. – Xeo Jun 21 '11 at 10:57
  • 1
    @Xeo: "It forces dummy to become initialized, because it is used" Not actually. It is used only if you call `get_dummy()` somewhere. Static members and global variables are initialized either before first statement of `main` or before first using of any object or function defined in the same translation unit as that static member or global variable. So it is totally allowed to not initialize Foo::dummy at all if you don't use any objects or functions from the same translation unit where template Foo specialization is instantiated. – Serge Dundich Jun 21 '11 at 17:11
  • @Serge: No, it counts as "used" just because it is there as a statement. That alone is enough to cause the compiler to initialize it, at least on every compiler I tested this on (MSVC & GCC. :P) – Xeo Jun 21 '11 at 17:29
  • @Xeo: <> What makes you think so? Any references to the standard? Read the chapter 12.6 ISO/IEC 14882-2003. What your tests show is exactly what implementation-dependent mean. Besides you don't really know what was the exact moment and reason of dynamic initialization of `Foo::dummy`. May be `struct Bar: Foo {...}` definition alone is enough (that will lead to Foo specialization instantiation). – Serge Dundich Jun 21 '11 at 19:57
  • @Serge: No, the definition alone is not enough. Take out the `(void)dummy` and you won't see the call: http://ideone.com/Kukds – Xeo Jun 21 '11 at 20:03
  • @Xeo: "Take out the (void)dummy and you won't see the call" I confirm that behavior. But still it is implementation dependent. Standard does not require `dummy` to be initialized even if you add `(void)dummy` to `Bar::get_dummy()`. The result for any particular implementation is not an argument at all. C++ standard statements are. – Serge Dundich Jun 22 '11 at 06:30
  • 1
    Title should read *instantiated*, not *initialized*... those terms are often incorrectly interchanged – M.M Jan 10 '20 at 05:14
  • 1
    that trick requires to have somwhere call `get_dummy` at least once? – dragonxlwang Jan 21 '21 at 02:31

6 Answers6

12

Consider:

template<typename T, T> struct value { };

template<typename T>
struct HasStatics {
  static int a; // we force this to be initialized
  typedef value<int&, a> value_user;
};

template<typename T>
int HasStatics<T>::a = /* whatever side-effect you want */ 0;

It's also possible without introducing any member:

template<typename T, T> struct var { enum { value }; };
typedef char user;

template<typename T>
struct HasStatics {
  static int a; // we force this to be initialized
  static int b; // and this

  // hope you like the syntax!
  user :var<int&, a>::value,
       :var<int&, b>::value;
};

template<typename T>
int HasStatics<T>::a = /* whatever side-effect you want */ 0;

template<typename T>
int HasStatics<T>::b = /* whatever side-effect you want */ 0;
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • The first one seems to work [on GCC](http://ideone.com/4t1mi), but is no good in MSVC10, no initialization happens. :/ The second one works on [neither GCC](http://ideone.com/s85LF) nor MSVC10. :( **Edit**: Oh wait, with C++0x enabled [it works on GCC](http://ideone.com/s85LF). Still no luck with MSVC... – Xeo Jun 23 '11 at 20:52
  • @Xeo it works for me without c++0x on gcc4.6. Don't have any other compiler to try, except clang. On clang the second one doesn't work either. PR is on the way. – Johannes Schaub - litb Jun 23 '11 at 20:57
  • 5
    Oh Gosh! what `user :var::value` does mean? – pure cuteness Jun 23 '11 at 21:00
  • 4
    @pure: It's an unnamed bitfield, effectively a `char : 0;` – Xeo Jun 23 '11 at 21:07
5

We can use a simple trick based on a declaration that must be instantiated with the class:

template<…>
struct Auto {
  static Foo foo;
  static_assert(&foo);
};
template<…> Foo Auto::foo=…;

Note that some compilers warn about the comparison to null; that can be avoided with &foo==&foo, (bool)&foo, or ((void)&foo,true) if needed.

Note also that GCC 9.0–9.2 don’t count this as an odr-use.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
0

Something like that comes to my mind:

// in some c++ file (to make i with internal linkage)
static int i = init_dummy(Foo<int>::dummy);

where init_dummy is defined like this:

int init_dummy(...)
{
  return 1;
}

Due to variable args you can put more initializations there like:

static int i = init_dummy(Foo<int>::dummy, Foo<double>::dummy, Foo<whatever>::dummy);
ovanes
  • 5,483
  • 2
  • 34
  • 60
0

I recently bumped into this. The official solution would be explicit template instantiation, described here.

In the above case, the statement should look like this:

template class Foo::Bar;
Nir
  • 69
  • 5
-1

How are you checking the value set by Bar. I changed your code and added another function in bar as:

....
static char const get_dummy(int){return Foo<Bar>::dummy;}
....

and it is giving me exactly the expected result. I may not be understanding correctly, what do you exactly want to achieve ?

Static members are shared among the objects so their scope must be resolved at access. that is why we use :: by telling compiler explicitly that this is the member of the class we want to access.

Wug
  • 12,956
  • 4
  • 34
  • 54
Orochi
  • 395
  • 4
  • 13
-2

Is there any way to force dummy to be initialized (effectively calling register_) without any instance of Bar or Foo (no instances, so no constructor trickery)?

Wouldn't this be sufficient?

std::cout << Foo<int>::dummy;
StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • I hope there are other ways than having the user to explicitly state the member. :| – Xeo Jun 21 '11 at 06:10