Question

I'm using a library that has a class with an init function distinct from its constructor. Every time I make a new instance I need to call, for example:

MyClass a;
a.init();

Since init is not const, this prevents me from creating const instances (I can't write const MyClass a). Is there some way to call init and then declare from "here on out" (I guess for the remainder of the scope) my variable is const?

This works, but relies on not touching the original variable:

MyClass dont_touch;
dont_touch.init();
const MyClass & a = dont_touch;
Was it helpful?

Solution

If you're using C++11 you could use a lambda function

const MyClass ConstantVal = []{ 
    MyClass a;
    a.init(); 
    return a; 
}();

This allows you to keep the initialization in place while never giving outside access to the mutable object. see also: http://herbsutter.com/2013/04/05/complex-initialization-for-a-const-variable/

OTHER TIPS

You can create a wrapper class and use that instead.

If MyClass has a virtual destructor you can feel safe deriving from it like this:

class WrapperClass : public MyClass 
{
public:
    WrapperClass()
    {
        init(); // Let's hope this function doesn't throw
    }
};

Or write a class that contains the MyClass instance

class WrapperClass
{
public:
    WrapperClass()
    {
        m_myClass.init(); // Let's hope this function doesn't throw
    }
    operator MyClass&() {return m_myClass;}
    operator const MyClass&() const {return m_myClass;}
private:
    MyClass m_myClass;
};

Or write a template to solve this general problem using one of the two solutions above: eg.

template <class T> class WrapperClass : public T
{
public:
    WrapperClass()
    {
        T::init();
    }
};

typedef WrapperClass<MyClass> WrapperClass;

Create a function that wraps the first two lines and gives you an object that is ready to go.

MyClass makeMyClass()
{
   MyClass a;
   a.init();
   return a;
}

// Now you can construct a const object or non-const object.
const MyClass a = makeMyClass();
MyClass b = makeMyClass(); 

Update

Using makeMyClass() involves construction and destruction of a temporary object everytime the function is called. If that becomes a significant cost, makeMyClass() can be altered to:

MyClass const& makeMyClass()
{
   static bool inited = false;
   static MyClass a;
   if ( !inited )
   {
     inited = true;
     a.init();
   }
   return a;
}

It's usage, as described earlier, will continue to work. In addition, once can also do this:

const MyClass& c = makeMyClass();

You can actually do it quite simply, even without C++11 and lambdas:

const MyClass a;
{
    MyClass _a;
    _a.init();
    std::swap(const_cast<MyClass&>(a), _a);
}

The use of const_cast is admittedly a bit of a hack, but it won't break anything as const is quite a weak specifier. At the same time, it is quite efficient, as the MyClass object is only swapped, not copied (most reasonable expensive-to-copy objects should provide a swap function and inject an overload of std::swap).

Without the cast, it would require a helper:

struct Construct_Init {
    operator MyClass() const
    {
        MyClass a;
        a.init();
        return a;
    }
};
const MyClass a = Construct_Init();

This can be like this in a function (the Construct_Init structure needs not be declared at namespace scope), but it is a bit longer. The copy of the object may or may not be optimized away using copy elision.

Note that in both cases, the return value of init() is lost. If it returns a boolean where true is success and false is failure, it is better to:

if(!a.init())
    throw std::runtime_error("MyClass init failed");

Or just make sure to handle the errors appropriately.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top