1

I'm experiencing a weird issue in which I need to have a default constructor for my template class to work, but some of the classes I'd like to use with the template cannot have default constructors.

What I mean by some classes cannot have default constructors is they have a const reference field, hence, they must be set in the constructor/initialization list. If I have a default constructor, there is nothing to set these references to.

So, I guess there are two possible solutions to this: Find a way to allow for non-default constructors in the template class sometimes or find a way to set the const reference fields in a default constructor.

How do I resolve this?

Here's my template code:

#pragma once

template<class T>
class Singleton
{
public:
    static T* getInstance()
    {
        if(!instance) instance = new T;
        return instance;
    }
static void setInstance(T* inst)
{
    instance = inst;
}
protected:
    Singleton();
    ~Singleton();
private:
    Singleton(Singleton const&);
    Singleton& operator=(Singleton const&);
    static T* instance;
};

template<class T> T* Singleton<T>::instance = 0;

Here's where I set the const reference (in a .cpp):

InputHandler::InputHandler(const sf::Input& inputObject)
    : input(inputObject)
{

And input is defined in the header:

const sf::Input& input;

I have to use a constant reference because of this area of code (in a different file):

const sf::Input& input = window.GetInput();
InputHandler* inputHandler = new InputHandler(input);
Singleton<InputHandler>::setInstance(inputHandler);

GetInput() is not one of my methods, and it must return a const sf::Input&

Petwoip
  • 1,241
  • 2
  • 16
  • 25
  • Why are you using reference members? – R. Martinho Fernandes Nov 08 '11 at 08:20
  • 1
    Your question cannot be answered without seeing the code. Add the relevant parts of the template class. – Jan Hudec Nov 08 '11 at 08:22
  • 2
    *Why* does the template need it's argument to have default constructor? Without knowing that it's not possible to decide whether the template should be modified not to require it or the classes modified to have it. – Jan Hudec Nov 08 '11 at 08:29
  • I added my code to the question, so I hope it's more clear what's going on now. – Petwoip Nov 08 '11 at 16:35
  • I would seriously consider a redesign. Here are some [thoughts](http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) on the Singleton antipattern. – Mike Seymour Nov 08 '11 at 20:31

3 Answers3

2

If you have a reference member, then you cannot (sensibly) make your class default-constructible or assignable, since references must be initialised, and can't be reassigned.

If you need those properties from the class, then you probably want the member to be a pointer, initialised to null by default and assignable later if needed.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
  • I realize that you wrote this answer before I supplied the code, but now I hope it's clear that I can't use a pointer for this (I wish I could) – Petwoip Nov 08 '11 at 18:26
  • It's not clear at all that you can't use a pointer. Are you saying that you think `InputHandler::input` must be a reference, not a pointer, because it's initialised from a reference? Just make it a pointer, and initialise it with `input(&inputObject)`. – Mike Seymour Nov 08 '11 at 20:13
  • @Petwoip: Although you might be better off seriously rethinking your bizarre twist on the Singleton antipattern. If you simply create an `InputHandler` and pass references to any other classes that need it, then you won't need a default constructor at all. As a bonus, you'll also fix the memory leak, the thread safety issues, and the maintenance issues from the excessive coupling that comes from using global variables. – Mike Seymour Nov 08 '11 at 20:21
2

You're invoking the default constructor in getInstance with:

instance = new T;

What do you expect to happen if someone calls getInstance before setInstance?

If it's an error to call getInstance before setInstance, then getInstance should assert or throw or return NULL if called before instance is set. Just get rid of the new T, and your code will compile.

If it's not an error to call getInstance before setInstance, then you don't really have a singleton. Early callers would receive a default constructed object and later callers would get the one set later. That's two instances and therefore not a singleton.

Adrian McCarthy
  • 41,073
  • 12
  • 108
  • 157
  • I figured for some objects it would be convenient to simply call getInstance without having to call setInstance at all because then I wouldn't have to worry about instance being null when calling getInstance from various areas of my project. I wouldn't ever have a situation where you do both, but I guess the compiler won't know that. If I can't get that to work I'll just have to be more careful about setting/getting instances in the correct order. – Petwoip Nov 08 '11 at 18:41
1

You could perhaps have the Singleton constructor set the instance pointer, i.e.

Singleton::Singleton()
{
    assert(instance == nullptr);
    instance = static_cast<T*>(this);
}

You can then remove the setInstance function from the interface and getInstance can simply return instance.

In response to the comment, I was assuming that classes were being defined like this:

class MyClassThatShouldBeASingleton : public Singleton<MyClassThatShouldBeASingleton>
{
};

Of course, this does mean that you need to explicitly set up such a singleton rather than it instantiating automatically as in the original code. It's not necessarily how you'd want all singletons to work, just one way of solving the problem.

  • I'm away from my home computer which has the project, so I can't test this right now. I'm curious how this could work, because it doesn't seem to call the T constructor at all. Maybe I'm misunderstanding something about templates or static_cast. – Petwoip Nov 08 '11 at 18:48
  • That won't compile (and would be nonsense if it did), since `this` is a pointer to `Singleton`, which is an unrelated type to `T`. – Mike Seymour Nov 08 '11 at 20:26