Reputation: 6264
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;
Upvotes: 21
Views: 679
Reputation: 11031
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.
Upvotes: 1
Reputation: 206667
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();
Upvotes: 5
Reputation: 741
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/
Upvotes: 16
Reputation: 3713
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;
Upvotes: 9