Search code examples
c++classtemplatesvariantany

Class with data member that can be one of two types


Probably best explained with some code:

class MyClass {
  public:
    MyClass(const std::string& d1, const std::string& d2, const std::vector<AorB>& d3) : data1(d1), data2(d2), data3(d3) {}

    std::string getData1();
    std::string getData2();
    std::vector<AorB> getData3();

  private:
    std::string data1;
    std::string data2;
    std::vector<AorB> data3;
}

int main() {
  MyClass myClassA("d1", "d2", std::vector<A>());
  MyClass myClassB("d1", "d2", std::vector<B>());

  A myA = myClassA.getData3();
  B myB = myClassB.getData3();
}

This workflow "almost" works when using boost variants or boost any, but the thing I'm trying to avoid is the call to boost::get on the result of getData3 to get the actual type. In other words, I don't want the consumer of MyClass to have to know whether A or B is stored in data3. I just want them to be able to call getData3() which is whatever type it was passed upon creation.

I think it's possible through templates with template specialization/recursive inheritence, but I can't quite figure out how to get that working. Maybe it will look something like this?

class MyClass {
  public:
    template <typename AorB>
    MyClass(const std::string& d1, const std::string& d2, const std::vector<AorB>& d3) : data1(d1), data2(d2), data3(d3) {}

    std::string getData1();
    std::string getData2();

    template <typename AorB>
    std::vector<AorB> getData3();

  private:
    std::string data1;
    std::string data2;

    template <typename AorB>
    std::vector<AorB> data3;
  }

  int main() {
    MyClass myClassA<A>("d1", "d2", std::vector<A>());
    MyClass myClassB<B>("d1", "d2", std::vector<B>());

    A myA = myClassA.getData3();
    B myB = myClassB.getData3();
  }

However this won't work because we can't have non-static template class members.


Solution

  • To do what you are attempting, you would need to apply the template to MyClass as a whole, eg:

    template <typename AorB>
    class MyClass {
      public:
        MyClass(const std::string& d1, const std::string& d2, const std::vector<AorB>& d3) : data1(d1), data2(d2), data3(d3) {}
    
        std::string getData1();
        std::string getData2();
        std::vector<AorB> getData3();
    
      private:
        std::string data1;
        std::string data2;
        std::vector<AorB> data3;
    };
    
    int main() {
      MyClass<A> myClassA("d1", "d2", std::vector<A>());
      MyClass<B> myClassB("d1", "d2", std::vector<B>());
    
      A myA = myClassA.getData3();
      B myB = myClassB.getData3();
    }