编辑:
您可以使用 C++20 概念(老实说,首选)来实现它,但正如您在标签中提到的 C++17,您需要使用丑陋的方式,即void_t。 Demo
基本上所有的 Foo 实例都应该共享一些共同的“标签”、“标记”等,你可以命名它。
受保护的派生以防止不需要的类型转换,using 将标签提升到公开可见性,然后在 void_t 内部检查其公开存在。
#include <type_traits>
#include <iostream>
struct FooBase
{
struct FooTag{};
};
template<typename ...Args>
struct Foo : protected FooBase
{
using FooBase::FooTag;
};
using FooIF = Foo<int, float>;
using FooIU = Foo<int, unsigned>;
using FooC = Foo<char>;
struct BarPub : public FooIF {};
struct BarPrv : private FooIU {};
struct BarPrt : protected FooC {};
struct Baz {};
template<typename T, typename=void>
struct is_publicly_derived_from_any_foo : std::false_type
{};
template<typename T>
struct is_publicly_derived_from_any_foo<T,
std::void_t<decltype(typename T::FooTag{})>>
: std::true_type
{};
int main(int, char*[])
{
std::cout << is_publicly_derived_from_any_foo<FooIF>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<FooC>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<FooIU>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<Baz>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<BarPub>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<BarPrv>::value << "\n";
std::cout << is_publicly_derived_from_any_foo<BarPrt>::value << "\n";
return 0;
}
下面的旧答案(不完全匹配 OP 的用例)以防万一有人需要它:
您可以利用这样一个事实,即如果某些东西是从一个类公开派生的,那么它也可以转换为它,即转换为它的基类型。
现在,根据您在检测 Foo 方面的确切需要,代码可能会有所不同,但通常应该看起来像这样:
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;
}