17

I wrote a singleton template class just like boost does:

template <typename _T>
class Singleton
{ 
    public :
    static _T* Instance()
    {
        static _T obj;
        return &obj;
    }

protected :
    Singleton() {}

private :
    struct ObjectCreator
    {
        ObjectCreator()
        {
            Singleton<_T>::instance();
        }
    };

    static ObjectCreator object_creator;
};

template <typename _T>
typename Singleton<_T>::ObjectCreator Singleton<_T>::object_creator;

And I wrote the main function to test it.

#include "Singleton.h"
class A : public Singleton <A>
{
    public:
        int a;
};


int main()
{
    A::Instance()->a = 2;
}

I know I mistyped Instance in ObjectCreator's constructor, the weird thing is I can compile it correctly by gcc-4.4.7, then I used clang-6.0, it hit me with the typo.

I guess gcc can do some optimization, because I did not do anything with ObjectCreator, so it ignored the error code.

I have two questions:

  1. What should I do to make gcc report me that error (without changing my code), like add some compiler flag?
  2. If anyone has a more reliable explanation for this? Some official doc would do.

Ps: I am aware that boost would add a do_nothing function in ObjectCreate and call it from Singleton<_T>:: Instance() to avoid this optimization.

user7610
  • 25,267
  • 15
  • 124
  • 150
Riopho
  • 211
  • 1
  • 9

2 Answers2

6
  • What should I do to make gcc report that error (without changing my code), like add some compiler flag?

You could add an explicit instantiation template class Singleton<float>; (I just randomly picked float as type, but you could choose anything more appropriate) to force GCC to check for syntax. See https://gcc.godbolt.org/z/ii43qX for an example.

If you simply want the check, you could also put this explicit instanciation to a separate compilation unit by adding another cpp-file to your project.

However, doing an explicit instanciation is stronger than the implicit instanciation, as all members and methods are going to be instanciated. This behavior might be anwanted (see standard library for examples).

  • If anyone has a more reliable explanation for this? Some official doc would do.

Static members are not initialized implicitly until it is used in a way that its definition is required (this is very different to the explicit instanciation).

@StoryTeller found the right paragraph in the standard

14.7.1 Implicit instantiation [temp.inst]

The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; and it causes the implicit instantiation of the definitions of member anonymous unions. Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; 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.

EDIT You should accept the answer of @StoryTeller as he correctly explained both aspects of your question first.

Community
  • 1
  • 1
  • I'm not sure [temp.res]/8 applies here. The point immediately after the definition of `Singleton` is the `;` at the end of the class definition. A hypothetical instantiation there won't ODR-use `object_creator`, so it need not be defined even, let alone its constructor. – StoryTeller - Unslander Monica Aug 28 '18 at 12:04
  • @StoryTeller I need to think about that. Is your main point that the instanciation does not guarantee a compilation error? –  Aug 28 '18 at 12:16
  • @StoryTeller Got it. I will double check later. Thanks! –  Aug 28 '18 at 12:25
  • @StoryTeller I tend to agree with you. Will remove that part from the answer for now and try to update this evening. –  Aug 28 '18 at 13:10
3

If I read the standard correctly, I don't believe your code is ill-formed (bar the use of a _T identifier). Clang going the extra mile is fantastic, but GCC isn't wrong to accept it as is.

The reason is your program only contains an implicit instantiation of your template. According to N19051 (emphasis mine):

14.7.1 Implicit instantiation [temp.inst]

1 ... The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions or default arguments, of the class member functions, member classes, static data members and member templates; and it causes the implicit instantiation of the definitions of member anonymous unions. Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; 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.

Nothing uses object_creator in a way that requires its definition to exist. As such only the declaration is ever checked. Furthermore, only the declaration of class ObjectCreator is required to be instantiated itself, not its definition (or the definition of its constructor). This is for the same reason one can define an extern variable of a forward declared class type:

extern class C c;

The above paragraph (and the fact nothing uses object_creator) only requires the type name and object name be instantiated, to produce an effect similar to the above external declaration.

As a result GCC never has to verify instance is valid. I would say that given the above paragraph, even if you didn't have a typo, it's quite possible object_creator doesn't do what you think it does. If your code worked, it's only because the function local static obj was initialized on first use (making ObjectCreator redundant).

As for why adding an explicit instantiation (like @P i suggested) immediately causes an error. We can see here:

14.7.2 Explicit instantiation [temp.explicit]

7 The explicit instantiation of a class template specialization also explicitly instantiates each of its members (not including members inherited from base classes) whose definition is visible at the point of instantiation and that has not been previously explicitly specialized in the translation unit containing the explicit instantiation.

When we do that, we recursively force everything to be instantiated, and as a result, checked.


1 - This is a 2005 draft. Very close to C++03, and therefore I believe appropriate given your use of GCC 4.4.7.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458