如何在模板函数中根据类型构造不同的对象?
How to construct different object according to their types in a template function?
这是我的问题,我有一个基础 class Base
,两个派生的 classes ClassA
和 ClassB
形式 Base
只接受两个参数,两个 classes ClassB0
和 ClassB1
派生自 ClassB
接受三个参数。我想根据传递给函数 create
的类型创建 class 实例,如果类型是从 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);
}
问题是 if
和 else
的分支都需要在编译时求值,尽管其中一个会在 运行 时求值。
您可以应用 constexpr if statement (C++17 起),其条件必须在编译时已知,并且 if
或 else
分支将是被丢弃,不会在编译时再次求值。
If the value is true
, then statement-false is discarded (if present), otherwise, statement-true is discarded.
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;
}
在 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;
}
也许我弄错了,听起来你想实现工厂模式。您有一个 class 层次结构,并希望基于键创建派生的 class。
您遇到的困难是构造函数具有不同数量的参数。
在下面的代码中,我使用可变模板和 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;
}
这是我的问题,我有一个基础 class Base
,两个派生的 classes ClassA
和 ClassB
形式 Base
只接受两个参数,两个 classes ClassB0
和 ClassB1
派生自 ClassB
接受三个参数。我想根据传递给函数 create
的类型创建 class 实例,如果类型是从 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);
}
问题是 if
和 else
的分支都需要在编译时求值,尽管其中一个会在 运行 时求值。
您可以应用 constexpr if statement (C++17 起),其条件必须在编译时已知,并且 if
或 else
分支将是被丢弃,不会在编译时再次求值。
If the value is
true
, then statement-false is discarded (if present), otherwise, statement-true is discarded.
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;
}
在 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;
}
也许我弄错了,听起来你想实现工厂模式。您有一个 class 层次结构,并希望基于键创建派生的 class。
您遇到的困难是构造函数具有不同数量的参数。
在下面的代码中,我使用可变模板和 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;
}