I have C++ code that boils down to something like the following:
class Foo{
bool bar;
bool baz;
Foo(const void*);
};
Foo::Foo(const void* ptr){
const struct my_struct* s = complex_method(ptr);
bar = calculate_bar(s);
baz = calculate_baz(s);
}
Semantically, the bar and baz member variables should be const, since they should not change after initialization. However, it seems that in order to make them so, I would need to initialize them in an initialization list rather than assign them. To be clear, I understand why I need to do this. The problem is, I can't seem to find any way to convert the code into an initialization list without doing one of the following undesirable things:
- Call
complex_method
twice (would be bad for performance) - Add the pointer to the Foo class (would make the class size needlessly large)
Is there any way to make the variables const while avoiding these undesirable situations?
If you can afford a C++11 compiler, consider delegating constructors:
As a general advice, be careful declaring your data members as
const
, because this makes your class impossible to copy-assign and move-assign. If your class is supposed to be used with value semantics, those operations become desirable. If that's not the case, you can disregard this note.You may use delegate constructor in C++11:
One option is a C++11 delegating constructor, as discussed in other answers. The C++03-compatible method is to use a subobject:
You can make
bar
andbaz
const, or make thesubobject
const, or both.If you make only
subobject
const, then you can calculatecomplex_method
and assign tobar
andbaz
within the constructor ofsubobject
:The reason that you can't mutate
const
members within a constructor body is that a constructor body is treated just like any other member function body, for consistency. Note that you can move code from a constructor into a member function for refactoring, and the factored-out member function doesn't need any special treatment.If you don't want to use the newfangled delegating constructors (I still have to deal with compiler versions that don't know about them), and you don't want to change the layout of your class, you could opt for a solution that replaces the constructor with
const void *
argument by a static member function returningFoo
, while having a private constructor that takes the output fromcomplex_method
as argument (that latter much like the delegating constructor examples). The static member function then does the necessary preliminary computation involvingcomplex_method
, and ends withreturn Foo(s);
. This does require that the class have an accessible copy constructor, even though its call (in thereturn
statement) can most probably be elided.