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:
#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;
}