Steven W. Klassen
Steven W. Klassen

Reputation: 1571

specialize a c++ template function when two types are the same

My use case is the following. Given an object I want a readable means of determining if that object is a subclass of another object. Obviously at the core is a call to dynamic_cast, but I want something more readable. So I have the following:

template <typename C, typename T>
bool isInstanceOf(const T& t) noexcept {
    if (typeid(t) == typeid(C)) {
        return true;
    }
    if (dynamic_cast<const C*>(&t) != nullptr) {
        return true;
    }
    return false;
}

This works as intended, but if I make a call where C and T are in fact the same type, I get a compiler warning on the dynamic_cast because the compiler knows it will never return null. This leads me to my question which is: can I write a specialized version of this that simply returns true if C and T are in fact the same type.

I tried the obvious

template <typename C>
inline bool isInstanceOf(const C& t) noexcept {
    return true;
}

but that gives me an error of "Call to 'isInstanceOf' is ambiguous."

This isn't really a high-priority item as I would never actually call isInstanceOf<B>(b) where I know b is of type B, but I have it in my unit tests for completeness and would like to see if there is a way to get the compiler to optimize it out without giving the warning.

In case it help, here is the warning message I receive:

In file included from Tests/rtti.cpp:15:0:
.build/Linux-x86_64/include/kss/util/rtti.hpp: In instantiation of ‘bool kss::util::rtti::isInstanceOf(const T&) [with C = {anonymous}::B; T = {anonymous}::B]’:
Tests/rtti.cpp:81:9:   required from here
.build/Linux-x86_64/include/kss/util/rtti.hpp:61:40: warning: the compiler can assume that the address of ‘t’ will never be NULL [-Waddress]
     if (dynamic_cast<const C*>(&t) != nullptr) {
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~

Upvotes: 0

Views: 1611

Answers (2)

Jarod42
Jarod42

Reputation: 217075

If you have C++17, you might use if constexpr:

template <typename C, typename T>
bool isInstanceOf(const T& t) noexcept {
    if constexpr (std::is_same<T, C>::value) {
        return true;
    } else {
        return dynamic_cast<const C*>(&t) != nullptr;
    }
}

Prior C++17, overloads and SFINAE (or tag dispatching) might do the job:

template <typename C>
bool isInstanceOf(const C& t) noexcept {
    return true;
}

template <typename C, typename T>
std::enable_if_t<!std::is_same<C, T>::value, bool>
isInstanceOf(const T& t) noexcept {
    return dynamic_cast<const C*>(&t) != nullptr;
}

Upvotes: 1

max66
max66

Reputation: 66190

Not exactly what you asked and mainly for fun but... what about a couple of template overloaded functions as follows

template <typename T>
bool isIstanceOf (T const &) noexcept
 { return true; }

template <typename>
bool isIstanceOf (...) noexcept
 { return false; }

that you have to call explicating the type T ?

But is more "is-convertible-to" than "is-instance-of".

The following is a full compiling example

#include <iostream>

template <typename T>
bool isIstanceOf (T const &) noexcept
 { return true; }

template <typename>
bool isIstanceOf (...) noexcept
 { return false; }

struct A
 { };

struct B : public A
 { };

int main()
 {
   A  a;
   B  b;

   std::cout << isIstanceOf<A>(a) << std::endl;    // print 1
   std::cout << isIstanceOf<A>(b) << std::endl;    // print 1
   std::cout << isIstanceOf<B>(a) << std::endl;    // print 0
   std::cout << isIstanceOf<B>(b) << std::endl;    // print 1
   std::cout << isIstanceOf<int>(a) << std::endl;  // print 0
   std::cout << isIstanceOf<int>(b) << std::endl;  // print 0
 }

Upvotes: 0

Related Questions