为 class 模板实现一个类型特征,该特征适用于实际的 class 模板和继承它的 classes

Implement a type trait for a class template that is true for the actual class template and classes that inherit it

我有一个像这样的元组式 class 模板

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

现在我需要实现这样的东西

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

IsFoo可以这样直接实现

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

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

现在,我还需要 IsFoo 为真,以防传递的类型公开派生自 Foo 的任何实例化,例如

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

也应该像上面第一个 if constexpr 分支中的 Foo 一样对待。

但是,我无法弄清楚如何正确实现我的 IsFoo 特征的模板特化,当 Derived 传递给它时该特征会起作用。但我敢肯定 Whosebug 知道怎么做!

编辑:我发现虽然项目使用的所有编译器都支持概念,但没有完整的 C++ 20 支持。所以我决定启用它,以便使用建议的基于概念的解决方案。

你可以这样做:

  #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");;
  }                                                         

已编辑: 您可以使用 C++20 概念(老实说,首选)来实现它,但正如您在标签中提到的 C++17,您需要使用丑陋的方式,即 void_tDemo 基本上 Foo 的所有实例都应该共享一些共同的“标签”、“标记”等,随便你怎么说。 受保护的派生以防止不需要的类型转换,using 将标签提高到 public 可见性,然后检查其 public 是否存在于 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 的用例)以防有人需要它:

您可以利用这一事实,如果某些东西 public 仅派生自 class,它也可以转换为它,即转换为它的基本类型。

现在,根据您在检测 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;
}

C++20 概念让事情变得更简单:

template <class... Ts>
struct Foo {};

template<class T>
concept IsFoo = requires(T& t){ 
  []<class... Ts>(Foo<Ts...>&){}(t);  
};

Demo.

我的 C++14 版本(可以很容易地调整到 C++11):

template <template<typename...> typename T, class... U>
void callWithTemplateBase(const T<U...>&);

template <typename T, template<typename...> typename Base, typename U = void>
struct IsInstanceOfTemplate : std::false_type {};

template <typename T, template<typename...> typename Base>
struct IsInstanceOfTemplate<
        T, 
        Base, 
        decltype(callWithTemplateBase<Base>(std::declval<T>()))> 
    : std::true_type {};

template <typename T, template<typename...> typename Base>
constexpr bool IsInstanceOfTemplate_v = IsInstanceOfTemplate<T, Base>::value;


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

template<typename T>
constexpr bool IsFoo_v = IsInstanceOfTemplate_v<T, Foo>;

https://godbolt.org/z/sv7jTPbE6