1

I am working in C++ and I have this problem.

class Container {
   Container(int which_type_to_use_a_b_or_c);

   A & getData(A & prototype);
   B & getData(B & prototype);
   C & getData(C & prototype);

private:
   A a;
   B b;
   C c;
   int type_used;

}

Only one of the A,B or C datatypes is actually used in a class Container instantiation. The constructor flag decides which one to use. When it's time to get a reference to the internal object, I need a getData routine which returns the reference of the different type. In order to disambiguate, I pass a "prototype" A() B() or C().

I don't really like this pattern, but I am unable to find anything better due to my limited palette in c++. Do you have any other approach available? please note that templating Container over A, B or C is not an option due to other constraints.

5
  • Do/Can A, B and C have a common parent?
    – superM
    Apr 10, 2013 at 8:45
  • @superM they have not, but we also can't use pointers, only references Apr 10, 2013 at 8:46
  • 7
    Long time I didn't use C++, but wouldn't this be a typical case where you would use templates? Apr 10, 2013 at 8:47
  • @thorstenmüller: it would, but I don't want to for other reasons. Apr 10, 2013 at 8:48
  • 10
    sounds like you have a lot of constraints you haven't told us then, perhaps you should share these
    – jk.
    Apr 10, 2013 at 8:58

3 Answers 3

8

You could use a discriminated union, like Boost.Variant

class Container {
    typedef boost::variant<A,B,C> ABC;
    enum Type { UseA, UseB, UseC };

    explicit Container(Type t);

    ABC & getData();

private:
    ABC value;
}

but frankly this feels like an X-Y problem. What are you actually trying to accomplish?


NB. you said in a comment

... we also can't use pointers, only references

you may be interested to know that runtime polymorphism works just fine with references (even if it doesn't help here).

7
  • Yes, but you can't leave a reference pointing to nothing, or not assign it, or have a reference to a base class as a member variable. Some patterns require that using pointers. Apr 10, 2013 at 9:25
  • 1
    A reference has to be bound, yes, but why can't you have a reference to a base class as a member variable? Admittedly you still need to create the instance somewhere. Perhaps you could explain why you're not allowed pointers?
    – Useless
    Apr 10, 2013 at 9:27
  • 1
    Wait, so is changing the type after initialization a requirement now? And - I have no idea what you mean by generic programming if it precludes pointers.
    – Useless
    Apr 10, 2013 at 9:50
  • 1
    You can certainly use both runtime and compile-time polymorphism mixed together. I get the feeling there are lot of constraints & decisions going unstated in this question.
    – Useless
    Apr 10, 2013 at 12:18
  • 2
    You prefer generic programming but don't want to use templates in the solution?
    – bstamour
    Apr 10, 2013 at 14:20
3

It looks a lot like Boost.Variant which might either replace your class or help implementing it easily.

Basically it provide a way to allocate memory that can contain either A, B, C which are defined at compile-time. When you need to get the data, it's easy to retrieve what you want. You can check dynamically which time is stored, and if the user code already know which type should be available, it can just extract the value and get an exception if it's not the expected type.

3
  • This is an interesting pattern indeed. +1 Apr 10, 2013 at 9:04
  • I was gonna suggest using union, but this looks nicer. Apr 10, 2013 at 9:05
  • @MartinWickman Yes it's basically a type-safe union. Boost.Any is basically a type-safe void*.
    – Klaim
    Apr 10, 2013 at 9:50
2

in addition to the concept of Boost::Variant or simple unions, this is a problem that may be better solved using a template. (and I know you don't want to use templates, but if you won't tell us why, I'm going to answer anyway - it might help someone else new to C++ get the right answer)

So you'd create a class where the type you're containing is not passed in as a flag, but is passed as a 'compiler instruction' to generate a class specifically for the type you want to use.

eg.

template <class T>
class Container{
    T the_type_thats_used;
  public:
    T & getData(T & prototype);
    typedef T type_used;
};

this is what templates are used for, a good example is the STL that has a load of container classes that work this way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.