简体   繁体   中英

Templated class assignment in C++

This might be a very basic question, but I searched and couldn't find anything.

I have some code that looks like this:

if(var == 1) {
  MyClass<A> x();
  x.doThing1();
  x.doThing2();
  x.doThing3();
  ...
  x.doThing10();
} else if(var == 2) {
  MyClass<B> x();
  x.doThing1();
  x.doThing2();
  x.doThing3();
  ...
  x.doThing10();
} else {
  MyClass<C> x();
  x.doThing1();
  x.doThing2();
  x.doThing3();
  ...
  x.doThing10();
}

What I'd like to do instead is to cut down the number of lines, eg:

// obviously non-working code
MyClass<auto> x;
if(var == 1) {
  x<A>();
} else if(var == 2) {
  x<B>();
} else {
  x<C>();
}
x.doThing1();
x.doThing2();
x.doThing3();
...
x.doThing10();

Is something like this possible?

Well, you can just refactor it into something like:

template <class T>
void doThings()
{
  MyClass<T> t;
  t.doThing1();
  // ...
  t.doThing10();
}

// dispatch
switch (var)
{
case 1: doThings<A>(); break;
case 2: doThings<B>(); break;
// ...
}

There are a few things you can do.

In the general case, if you don't mind dynamic memory allocation, you can take define the interface in a virtual base class, implement the interface in MyClass, using whatever you need from the template parameter. Something along these lines:

#include <memory>
#include <iostream>

class Base
{
public:
    virtual void doThing1() const = 0;
    virtual void doThing2() const = 0;
    virtual void doThing3() const = 0;
};

template <class T>
class MyClass: public Base
{
public:
    void doThing1() const {std::cout << T::foo << ".doThing1()" << std::endl;}
    void doThing2() const {std::cout << T::foo << ".doThing2()" << std::endl;}
    void doThing3() const {std::cout << T::foo << ".doThing3()" << std::endl;}
};
struct A {static const char foo = 'A';};
struct B {static const char foo = 'B';};
struct C {static const char foo = 'C';};

std::unique_ptr<Base> makeMyClassInstance(const int var)
{
    switch (var)
    {
        case 1: return std::make_unique<MyClass<A> >();
        case 2: return std::make_unique<MyClass<B> >();
        case 3: return std::make_unique<MyClass<C> >();
        default: throw std::runtime_error("unsupported");
    }
}
int main()
{
    int var = 1;
    const auto x = makeMyClassInstance(var);
    x->doThing1();
    x->doThing2();
    x->doThing3();
}

If var is not a variable but can be known at compile time, you can have a more radical design. I am skipping the MyClass in this case, but you will get the idea:

#include <type_traits>
#include <iostream>
#include <tuple>

struct A{void foo() {std::cout << "A" << std::endl;}};
struct B{void foo() {std::cout << "B" << std::endl;}};
struct C{void foo() {std::cout << "C" << std::endl;}};

template <int I>
struct SelectType
{
    typedef typename std::remove_reference<decltype(std::get<I-1> (std::make_tuple(A(), B(), C())))>::type type;
};

template <int I, class T = typename SelectType<I>::type>
T makeMyClassInstance() {return T();}

int main()
{
    auto x1 = makeMyClassInstance<2>();
    x1.foo(); 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM