Here is my question, I have a base class Base
, two derived classes ClassA
and ClassB
form Base
taking only two arguments, and two classes ClassB0
and ClassB1
derived from ClassB
taking three arguments. I want to create class intance accroding to the type passed to the function create
, if the type is derived from ClassB
, the first argument will be filled with 10. The compiler always warn that there is no matching constructors.
#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);
}
The problem is that both the branch of if
and else
need to be evaluated at compile-time, despite of which one would be evaluated at run-time.
You can apply constexpr if statement (since C++17), for which the condition must be known at compile-time, and either the if
or else
branch would be discarded and won't be evaluated at compile-time again.
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;
}
Before C++17, you can apply overloading with SFINAE. e.g.
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;
}