Home > front end >  Implement a type trait for a class template that is true for the actual class template and classes t
Implement a type trait for a class template that is true for the actual class template and classes t

Time:11-26

I have a tuple-like class template like this

template <class... T>
struct Foo {}

Now I need to implement something like this

template <class T>
void bar (const T& t)
{
    if constexpr (IsFoo<T>::value)
        // treat it as Foo
    else
        // a generic solution
}

IsFoo can be implemented straightforward like this

template <class T>
struct IsFoo : std::false_type {}

template <class... T>
struct IsFoo<Foo<T...>> : std::true_type {}

Now, I also need IsFoo to be true in case the type passed is publicly derived from any instantiation of Foo, e.g.

struct Derived : public Foo<int, float> {}

should also be treated like a Foo in the first if constexpr branch above.

However, I can't figure out how to properly implement a template specialisation of my IsFoo trait that would work when Derived is passed to it. But I'm sure Stackoverflow knows how to!

CodePudding user response:

You can do it this way:

  #include <type_traits>
           
  template <class A, class B> class Foo {};
           
  class Bar : public Foo<int, float> {};
           
  template <class A, class B> 
  std::true_type checkFoo(const Foo<A,B>&);
      
  std::false_type checkFoo(...);
      
  int main()
  {   
      Bar bar;
      int baz;
      static_assert(decltype(checkFoo(bar))::value, "bar test failed");;
      static_assert(!decltype(checkFoo(baz))::value, "baz test failed");;
  }                                                         

CodePudding user response:

You can take advantage of the fact, that if something is publicly derived from a class, it is also convertible to it, i.e. to its base type.

Now, depending what you exactly need in terms of detecting Foo the code might differ, but generally it should look - like this:

Demo

#include <iostream>
#include <type_traits>
           
template <class... T>
struct Foo {};

template <class T>
struct IsFoo : std::false_type {};

template <class... T>
struct IsFoo<Foo<T...>> : std::true_type {};


class BarPub : public Foo<int, float> {};
class BarPriv : private Foo<int, float> {};
class BarProt : protected Foo<int, float> {};
using FooIF = Foo<int, float>;


template<typename T, typename U>
struct is_public_base_of 
: std::conjunction<
        std::is_convertible<T, U>,
        std::is_base_of<U, T>>
{};
//extra disjunction with std::is_same<U,T>
//for fundamental types if it's also needed

template<typename T, typename ...Args>
struct typed_foo_or_publicly_derived:
    is_public_base_of<T, Foo<Args...>>
{}; 

template<typename T, typename ...Args>
struct any_foo_or_publicly_derived:
    std::disjunction<
        IsFoo<T>,
        is_public_base_of<T, Foo<Args...>>>
{}; 

int main()
{   
    std::cout << is_public_base_of<BarPub, Foo<int, float>>::value << "\n";
    std::cout << is_public_base_of<BarPriv, Foo<int, float>>::value << "\n";
    std::cout << is_public_base_of<BarProt, Foo<int, float>>::value << "\n";
    std::cout << is_public_base_of<FooIF, Foo<int, float>>::value << "\n";
    std::cout << typed_foo_or_publicly_derived<BarPub, int, float>::value << "\n";
    std::cout << typed_foo_or_publicly_derived<BarPriv, int, float>::value << "\n";
    std::cout << typed_foo_or_publicly_derived<BarProt, int, float>::value << "\n";
    std::cout << typed_foo_or_publicly_derived<FooIF, int, float>::value << "\n";
    return 0;
}
  • Related