如何在模板函数中根据对象的类型构造不同的对象?

问题描述 投票:3回答:2

[这是我的问题,我有一个基类Base,两个派生类ClassAClassB形式Base仅采用两个自变量,以及两个类ClassB0ClassB1ClassB派生接受三个论点。我想创建一个类实例,该实例实例应传递给函数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
2个回答
5
投票
问题是,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

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

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

在下面的代码中,我使用可变参数模板和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; }

© www.soinside.com 2019 - 2024. All rights reserved.