【问题标题】:How to construct different object according to their types in a template function?如何根据模板函数中的类型构建不同的对象?
【发布时间】:2020-04-15 04:25:29
【问题描述】:

这是我的问题,我有一个基类Base,两个派生类ClassAClassB 形式Base 只接受两个参数,还有两个类ClassB0ClassB1 派生自@987654327 @ 接受三个参数。我想根据传递给函数create的类型创建类实例,如果类型派生自ClassB,第一个参数将填充10。编译器总是警告没有匹配的构造函数。

#include <iostream>
#include <type_traits>

class Base {};
class ClassA : public Base {
public:
  ClassA(int a, int b) : Base() {
    std::cout << "ClassA: " << a << " " << b << "\n\n";
  }
};

class ClassB : public Base {
public:
  ClassB(int a, int b, int c) : Base() {
    std::cout << "ClassB: " << a << " " << b << " " << c << "\n";
  }
};

class ClassB0 : public ClassB {
public:
  ClassB0(int a, int b, int c) : ClassB(a, b, c) { 
    std::cout << "ClassB0: " << a << " " << b << " " << c << "\n\n";
  }
};
class ClassB1 : public ClassB {
public:
  ClassB1(int a, int b, int c) : ClassB(a, b, c) {
    std::cout << "ClassB1: " << a << " " << b << " " << c << "\n\n";
  }
};

template <typename T, typename ...Args>
T* create(Args&&... args) {
  T* comp = nullptr;

  if (std::is_base_of<ClassB, T>::value) {
    std::cout << "True ";
    comp = new T(10, std::forward<Args>(args)...);
  } else {
    std::cout << "False ";
    comp = new T(std::forward<Args>(args)...);
  }

  return comp;
}

int main() {
  create<ClassA>(1, 2);
  create<ClassB0>(2, 3);
  create<ClassB1>(2, 3);
}

【问题讨论】:

    标签: c++ templates variadic-templates parameter-pack


    【解决方案1】:

    问题是ifelse 的分支都需要在编译时进行评估,尽管其中一个会在运行时进行评估。

    您可以应用constexpr if statement(C++17 起),其条件必须在编译时已知,并且ifelse 分支将被丢弃且不会在再次编译。

    如果值为true,则丢弃statement-false(如果存在),否则丢弃statement-true

    template <typename T, typename ...Args>
    T* create(Args&&... args) {
      T* comp = nullptr;
    
      if constexpr (std::is_base_of<ClassB, T>::value) {
      // ^^^^^^^^^
        std::cout << "True ";
        comp = new T(10, std::forward<Args>(args)...);
      } else {
        std::cout << "False ";
        comp = new T(std::forward<Args>(args)...);
      }
    
      return comp;
    }
    

    LIVE

    在 C++17 之前,您可以使用 SFINAE 应用重载。例如

    template <typename T, typename ...Args>
    typename std::enable_if<std::is_base_of<ClassB, T>::value, T*>::type
    create(Args&&... args) {
      T* comp = nullptr;
      std::cout << "True ";
      comp = new T(10, std::forward<Args>(args)...);
      return comp;
    }
    template <typename T, typename ...Args>
    typename std::enable_if<!std::is_base_of<ClassB, T>::value, T*>::type
    create(Args&&... args) {
      T* comp = nullptr;
      std::cout << "False ";
      comp = new T(std::forward<Args>(args)...);
      return comp;
    }
    

    LIVE

    【讨论】:

    • 抱歉好奇......在 C++11 中是怎么回事?
    • @abdan 答案已修改。
    【解决方案2】:

    也许我弄错了,但听起来你想实现工厂模式。您有一个类层次结构并希望基于键创建派生类。

    您遇到的困难是构造函数的参数数量不同。

    在下面的代码中,我用可变参数模板和std::any完全解决了你的问题。

    请参阅下面的完整工作示例:

    #include <iostream>
    #include <map>
    #include <utility>
    #include <any>
    
    
    // Some demo classes ----------------------------------------------------------------------------------
    struct Base {
        Base(int d) : data(d) {};
        virtual ~Base() { std::cout << "Destructor Base\n"; }
        virtual void print() { std::cout << "Print Base\n"; }
        int data{};
    };
    struct Child1 : public Base {
        Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
        virtual ~Child1() { std::cout << "Destructor Child1\n"; }
        virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
    };
    struct Child2 : public Base {
        Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
        virtual ~Child2() { std::cout << "Destructor Child2\n"; }
        virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
    };
    struct Child3 : public Base {
        Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
        virtual ~Child3() { std::cout << "Destructor Child3\n"; }
        virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
    };
    
    
    
    using UPTRB = std::unique_ptr<Base>;
    
    
    template <class Child, typename ...Args>
    UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }
    
    // The Factory ----------------------------------------------------------------------------------------
    template <class Key, class Object>
    class Factory
    {
        std::map<Key, std::any> selector;
    public:
        Factory() : selector() {}
        Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}
    
        template<typename Function>
        void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };
    
        template <typename ... Args>
        Object create(Key key, Args ... args) {
            if (selector.find(key) != selector.end()) {
                return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
            }
            else return nullptr;
        }
    };
    
    int main()
    {
        Factory<int, UPTRB> factory{
            {1, createClass<Child1, int, std::string>},
            {2, createClass<Child2, int, char, long>}
        };
        factory.add(3, createClass<Child3, int, long, char, std::string>);
    
    
        // Some test values
        std::string s1(" Hello1 "); std::string s3(" Hello3 ");
        int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;
    
        UPTRB b1 = factory.create(1, 1, s1);
        UPTRB b2 = factory.create(2, 2, '2', 2L);
        UPTRB b3 = factory.create(3, 3, 3L, '3', s3);
    
        b1->print();
        b2->print();
        b3->print();
        b1 = factory.create(2, 4, '4', 4L);
        b1->print();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-05-16
      • 2020-01-17
      • 2013-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多