1

I need to store objects of exactly two types in a vector, the both types have almost nothing in common.

After storing them in the vector, I want to iterate over that vector and perform an action, depending on the type.

my thoughts so far:

  • Polymorphism. Overkill, and wouldn't help me much, as I probably would do this:

    if(dynamic_cast<T1>() != nullptr) {
        ...
    } else {
       ...
    }
    
  • Merge both types (methods and fields) and add a boolean, representing if its type 1 or 2.

Both patterns seem totally clumsy to me, there is propably a total simple solution, I simply don't see.

The first type is something like this :

struct PatternMatch {
  int length;
  int indexInDict;
}

The second one:

struct NoMatch {
  std::string rawChars;
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
J0hnD0e
  • 87
  • 6
  • Why don't you create a `class` that has 2 member variables . One is `PatternMatch` and the second is `NoMatch`. Then create a vector that holds objects of that class ? – KostasRim Aug 27 '16 at 10:12
  • 2
    If you use inheritance and virtual functions, then you won't need to do any `dynamic_cast`. In fact, that's what polymorphism is all about (while `dynamic_cast` represents the exact opposite approach). – barak manos Aug 27 '16 at 10:14
  • 2
    Why not have two vectors? – Kerrek SB Aug 27 '16 at 10:19
  • 1
    I think we would need to know more about what you are going to do with these two types to have a hope of spotting an elegant solution tbh. – Galik Aug 27 '16 at 10:33

2 Answers2

5

Use boost::variant or any other "stack-based discriminated union container". I also suggest visiting the variant using lambdas.

// Either `A` or `B`.
using my_type = boost::variant<A, B>;

std::vector<my_type> my_vec;

// ...`emplace_back` stuff into `my_vec`...

auto visitor = make_lambda_visitor<void>(
    [](A&) { /* do something with A */ },
    [](B&) { /* do something with B */ }
);

for(auto& x : my_vec)
{
     boost::apply_visitor(visitor, x);
}

Note that C++17 will have std::variant.

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • i also though about visitor pattern, the only problem i cant solve (trivially) with that, is : i want to merge two successive NoMatches into one while iterating over the collection, but i didn't said that in the question – J0hnD0e Aug 27 '16 at 11:02
1

If you know that you have only two types and this number won't grow in future, a C-ish tagged union is enough and easy to use:

struct PatternMatch {
  int length;
  int indexInDict;
};

struct NoMatch {
  std::string rawChars;
};

struct TaggedUnion {
  enum { MATCH, NO_MATCH } flag;
  union {
    PatternMatch match;
    NoMatch noMatch;
  };
};

Now, you can create a vector of TaggedUnions and check the flag data member to find out the actual type of each element.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • i avoided unions by now ( was a little afraid of the magic )... but this seems to be the solution to my problem – J0hnD0e Aug 27 '16 at 10:55
  • @J0hnD0e There is no magic behind unions indeed and sometimes they are useful, so why not? :-) – skypjack Aug 27 '16 at 18:26