A trigger-specific constructor for a member in a flag-based member initialization list

I would like to call a specific constructor for a member variable based on the flags that I pass to the containing class constructor.

This is easiest if I start with a trivial example:

#include <boost/optional.hpp> #include <boost/none.hpp> #include <boost/utility/typed_in_place_factory.hpp> struct state { bool flag1; bool flag2; int value; }; class A { public: A() : _a() {} A(boost::none_t none) : _a() {} A(state& st) : _a(st.value) {} A(const A& copy) : _a(copy._a) {} private: boost::optional<int> _a; }; class B { public: B() : _b() {} B(boost::none_t none) : _b() {} B(state& st) : _b(st.value) {} B(const B& copy) : _b(copy._b) {} private: boost::optional<int> _b; }; class C { public: C() : _a(boost::none_t()), _b(boost::none_t()) {} C(state& st) : _a(st.flag1 ? st : boost::none_t()), _b(st.flag2 ? st : boost::none_t()) {} private: boost::optional<A> _a; boost::optional<B> _b; }; int main(void) { state f = { true, false, 10 }; C c(f); return 0; } 

So the idea is to call the constructor for A with state , but B with boost::none_t .
The above code does not compile because the ternary operator expects the same type for both capabilities, and state and boost::none_t are not the same type. Can anyone think of an elegant path around this?

I know two solutions:

  • Copy design, i.e.

    _a(st.flag1 ? A(st) : A(boost::none_t())), _b(st.flag2 ? B(st) : B(boost::none_t()))

  • Use pointers instead of boost::optional<> , then

    _a(st.flag1 ? new A(st) : new A(boost::none_t())), _b(st.flag2 ? new B(st) : new B(boost::none_t()))

# 2 is not so attractive as I try to avoid dynamic memory allocation (a real example contains dozens of members and deeply nested structures with a lot of members).
# 1 is also not attractive since I will need to build and then copy.

Is there a better alternative?

EDIT : state changed by members by construction, so I don’t want to build something with state if the flag is not set.

+6
c ++
source share
3 answers

It seems that using boost::optional<> complicates things, using it using a copy-based method seems to call both state constructors and copied objects A and B , even with full optimization.

However, if I define the C members as just A _a and B _b , the copy constructor is actually optimized, so

 _a(st.flag1? A(st) : A()), _b(st.flag2? B(st) : B()) 

Actually, apparently, the most optimal way to make this work, I just need to get along with the other functions that I used with boost :: optional <> (i.e., initialized or uninitialized).

+1
source share

You need to learn to trust your compiler optimizer for adequate embedding of this type or to limit yourself to using 99% of standard libraries and advanced C ++ tools.

Start by looking at the boost boost API:

 ... optional(bool condition, T const&); ... 

What does the matching constructor suggest to add ...

 A(bool condition, T const& v) : _a(condition, v) { } 

... and using it in your initialization ...

 _a(st.flag1, st.value) 
+3
source share

I expect the copy constructor approach to work, because, as said, it solves the type problem for the ternary operator. However, I do not know if the copy can be optimized.

However, in terms of readability, I would prefer:

 boost::optional<A> buildA(state& st) { return st.flag1 ? A(st) : A(); } boost::optional<B> buildB(state& st) { return st.flag2 ? B(st) : B(); } C::C(state& st): _a(buildA(st)), _b(buildB(st)) {} 

which is a bit more verbose. Copy optimization remains a good question.

Note: why are you using state& ? From this short example, a state const& would be better since the link was not taken from st .

0
source share

All Articles