Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trigger specific constructor for a member in the member initialisation list based on a flag

Tags:

c++

I would like to trigger a specific constructor for a member variable based on flags I pass to the containing class's constructor.

It's 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 trigger the constructor for A with state, but B with boost::none_t.
The above code does not compile because the ternary operator is expecting the same type for both possibilities, and state and boost::none_t are not the same type. Can anyone think of an elegant way around this?

I know of two solutions:

  1. Copy construct, i.e.

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

  2. 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 appealing, as I'm trying to avoid dynamic memory allocation (the real example has tens of members and deeply nested structures with more members).
#1 is also not appealing as I would need to construct and then copy.

Is there a better alternative?

EDIT: state is modified by the members on construction, so I don't want to construct something with state if the flag is not set.

like image 573
Nim Avatar asked Nov 04 '10 09:11

Nim


2 Answers

You have to learn to trust your compiler's optimiser to inline this kind of thing adequately, or you'll restrict yourself from using 99% of the Standard libraries and advanced C++ facilities.

Start by looking at the boost optional API:

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

Which suggests adding a matching constructor...

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

...and using it in your initialisation...

_a(st.flag1, st.value)
like image 85
Tony Delroy Avatar answered Nov 07 '22 16:11

Tony Delroy


It appears that using boost::optional<> is complicating things, using it with the copy constructed approach seems to cause both the state based and copy based constructors of A & B to be called - even with full optimization.

However, if I define the members of C as simply A _a and B _b, the copy constructor is actually optimised away, so doing

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

Is actually apparently the most optimal way of getting this to work, I just have to workaround the other functionality I was using from boost::optional<> (i.e. initialized vs. uninitialized).

like image 29
Nim Avatar answered Nov 07 '22 17:11

Nim