为 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_t
。 Demo
基本上 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 方面的确切需要,代码可能会有所不同,但通常它应该看起来像这样:
#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);
};
我的 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>;
我有一个像这样的元组式 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_t
。 Demo
基本上 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 方面的确切需要,代码可能会有所不同,但通常它应该看起来像这样:
#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);
};
我的 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>;