Reputation: 1243
I want to use template function for handling both polymorphic and non-polymorphic classes. Here are 3 basic classes.
class NotDerived
{
};
class Base
{
public:
virtual ~Base() {}
void base_method() {}
};
class Derived : public Base
{
};
Since NotDerived has no virtual functions, I can't use dynamic_cast, next comes template function:
template<class T>
auto foo(T& some_instance)
{
if (std::is_base_of_v<Base, T>)
{
//CASE_1: Works for d and b
/*some_instance.base_method();*/
//CASE_2: Works for d and b
/*auto lamb1 = [](T& some_instance) {some_instance.base_method(); };
lamb1(some_instance);*/
auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); };
lamb2(some_instance);
}
}
The main functions does this:
void main()
{
Derived d{};
Base b{};
NotDerived nd{};
foo(d);
foo(b);
foo(nd);
}
Now I understand why CASE_1 does not work in case of passing nd
variable, but what I can't understand is that I have to explicitly cast some_instance
in lamb2 function in order to call base_method.
Can someone explain why CASE_1, CASE_2 do not work, while CASE_3 works. By working I mean calling base_method if possible without dynamic_casting.
Also is it possible to use constexpr
for handling such cases of static polymorphism or compiled polymorphism(Hope it is legal to name it like this)
Upvotes: 1
Views: 237
Reputation: 4050
You should take a look into the official doc about SFINAE. The code inside your if will get compiled in any case, so that's why it wont compile.
If you are using C++17, you can replace it with a constexpr if, that will evaluate the information inside the if during compilation time and ignore the code if needed:
template<class T>
auto foo(T& some_instance) {
if constepxr (std::is_base_of_v<Base, T>) {
auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
lamb2(some_instance);
} else {
// Whatever you want to do
}
}
If you are using an older version of C++ a simple function overload may solve your problem:
template<class T>
auto foo(T& some_instance) {
// Do whatever
}
auto foo(Base& some_instance) {
auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
lamb2(some_instance);
}
As an alternative, you can use SFINAE mechanism to peek the right function by using std::enable_if
:
template <typename T>
typename std::enable_if<std::is_base_of<Base, T>::value, void>::type
foo() {
auto lamb2 = [](T& some_instance) {((Base&)some_instance).base_method(); }
lamb2(some_instance);
}
template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value, void>::type
foo() {
// Do whatever
}
Upvotes: 1
Reputation: 180415
Case 3 does not work. You are casting to an unrelated type and using that temporary to call the function which is undefined behavior.
The problem with using
if (std::is_base_of_v<Base, T>)
{
//...
}
is that everything in the ...
part needs to be able to compile. What you need is a way to only call that code when T
is the type you want. You can use constexpr if like
if constexpr (std::is_base_of_v<Base, T>)
{
some_instance.base_method();
}
else
{
// do something else since T isn't a Base or derived from Base
}
And now if std::is_base_of_v<Base, T>
is false then some_instance.base_method();
will be discarded and never compiled.
Upvotes: 5