std::is_base_of 和虚拟基地 class
std::is_base_of and virtual base class
有没有办法识别基地 class 是否是虚拟基地 class?
std::is_base_of 将识别一个基数 class,但我正在寻找类似 std::is_virtual_base_of 的东西来识别一个虚拟基数 class.
这是出于 SFINAE 的目的,我想在 std::is_virtual_base_of 为真时使用 dynamic_cast(性能较低),而在它为假时使用 static_cast(性能更高)。
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type {};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class... Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type {};
}
template<template<class...>class Z, class... Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
这让我们可以轻松地在模板应用程序上进行模块化 SFINAE。
template<class Dest, class Src>
using static_cast_r = decltype(static_cast<Dest>( std::declval<Src>() ));
template<class Dest, class Src>
using can_static_cast = can_apply< static_cast_r, Dest, Src >;
现在我们可以确定是否可以静态转换。
现在我们实现它:
namespace details {
template<class Dest, class Src>
Dest derived_cast_impl( std::true_type /* can static cast */ , Src&& src )
{
return static_cast<Dest>(std::forward<Src>(src));
}
template<class Dest, class Src>
Dest derived_cast_impl( std::false_type /* can static cast */ , Src&& src )
{
return dynamic_cast<Dest>(std::forward<Src>(src));
}
}
template<class Dest, class Src>
Dest derived_cast( Src&& src ) {
return details::derived_cast_impl<Dest>( can_static_cast<Dest, Src&&>{}, std::forward<Src>(src) );
}
测试代码:
struct Base { virtual ~Base() {} };
struct A : virtual Base {};
struct B : Base {};
struct Base2 {};
struct B2 : Base2 {};
int main() {
auto* pa = derived_cast<A*>( (Base*)0 ); // static cast won't work
(void)pa;
auto* pb = derived_cast<B*>( (Base*)0 ); // either would work
(void)pb;
auto* pb2 = derived_cast<B2*>( (Base2*)0 ); // dynamic cast won't work
(void)pb2;
}
使用 c++17 很容易实现。
#include <type_traits>
// First, a type trait to check whether a type can be static_casted to another
template <typename From, typename To, typename = void>
struct can_static_cast: std::false_type{};
template <typename From, typename To>
struct can_static_cast<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>>: std::true_type{};
// Then, we apply the fact that a virtual base is first and foremost a base,
// that, however, cannot be static_casted to its derived class.
template <typename Base, typename Derived>
struct is_virtual_base_of: std::conjunction<
std::is_base_of<Base, Derived>,
std::negation<can_static_cast<Base*, Derived*>>
>{};
// Proof that it works.
struct Base{};
struct NonVirtual: Base{};
struct Virtual: virtual Base{};
static_assert(is_virtual_base_of<Base, NonVirtual>::value == false);
static_assert(is_virtual_base_of<Base, Virtual>::value == true);
在 Godbolt 上查看:https://godbolt.org/z/jxjq5W
对于 c++11,它有点不干净。这是:
#include <type_traits>
template <typename From, typename To, typename = void>
struct can_static_cast: std::false_type{};
template <typename From, typename To>
struct can_static_cast<From, To, decltype(static_cast<To>(std::declval<From>()), void())>: std::true_type{};
template <typename Base, typename Derived, typename = void>
struct is_virtual_base_of: std::false_type{};
template <typename Base, typename Derived>
struct is_virtual_base_of<Base, Derived, typename std::enable_if<
std::is_base_of<Base, Derived>::value &&
!can_static_cast<Base*, Derived*>::value
>::type>: std::true_type{};
struct Base{};
struct NonVirtual: Base{};
struct Virtual: virtual Base{};
static_assert(is_virtual_base_of<Base, NonVirtual>::value == false);
static_assert(is_virtual_base_of<Base, Virtual>::value == true);
在 Godbolt 上查看:https://godbolt.org/z/qnT6aq
有没有办法识别基地 class 是否是虚拟基地 class?
std::is_base_of 将识别一个基数 class,但我正在寻找类似 std::is_virtual_base_of 的东西来识别一个虚拟基数 class.
这是出于 SFINAE 的目的,我想在 std::is_virtual_base_of 为真时使用 dynamic_cast(性能较低),而在它为假时使用 static_cast(性能更高)。
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type {};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class... Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type {};
}
template<template<class...>class Z, class... Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
这让我们可以轻松地在模板应用程序上进行模块化 SFINAE。
template<class Dest, class Src>
using static_cast_r = decltype(static_cast<Dest>( std::declval<Src>() ));
template<class Dest, class Src>
using can_static_cast = can_apply< static_cast_r, Dest, Src >;
现在我们可以确定是否可以静态转换。
现在我们实现它:
namespace details {
template<class Dest, class Src>
Dest derived_cast_impl( std::true_type /* can static cast */ , Src&& src )
{
return static_cast<Dest>(std::forward<Src>(src));
}
template<class Dest, class Src>
Dest derived_cast_impl( std::false_type /* can static cast */ , Src&& src )
{
return dynamic_cast<Dest>(std::forward<Src>(src));
}
}
template<class Dest, class Src>
Dest derived_cast( Src&& src ) {
return details::derived_cast_impl<Dest>( can_static_cast<Dest, Src&&>{}, std::forward<Src>(src) );
}
测试代码:
struct Base { virtual ~Base() {} };
struct A : virtual Base {};
struct B : Base {};
struct Base2 {};
struct B2 : Base2 {};
int main() {
auto* pa = derived_cast<A*>( (Base*)0 ); // static cast won't work
(void)pa;
auto* pb = derived_cast<B*>( (Base*)0 ); // either would work
(void)pb;
auto* pb2 = derived_cast<B2*>( (Base2*)0 ); // dynamic cast won't work
(void)pb2;
}
使用 c++17 很容易实现。
#include <type_traits>
// First, a type trait to check whether a type can be static_casted to another
template <typename From, typename To, typename = void>
struct can_static_cast: std::false_type{};
template <typename From, typename To>
struct can_static_cast<From, To, std::void_t<decltype(static_cast<To>(std::declval<From>()))>>: std::true_type{};
// Then, we apply the fact that a virtual base is first and foremost a base,
// that, however, cannot be static_casted to its derived class.
template <typename Base, typename Derived>
struct is_virtual_base_of: std::conjunction<
std::is_base_of<Base, Derived>,
std::negation<can_static_cast<Base*, Derived*>>
>{};
// Proof that it works.
struct Base{};
struct NonVirtual: Base{};
struct Virtual: virtual Base{};
static_assert(is_virtual_base_of<Base, NonVirtual>::value == false);
static_assert(is_virtual_base_of<Base, Virtual>::value == true);
在 Godbolt 上查看:https://godbolt.org/z/jxjq5W
对于 c++11,它有点不干净。这是:
#include <type_traits>
template <typename From, typename To, typename = void>
struct can_static_cast: std::false_type{};
template <typename From, typename To>
struct can_static_cast<From, To, decltype(static_cast<To>(std::declval<From>()), void())>: std::true_type{};
template <typename Base, typename Derived, typename = void>
struct is_virtual_base_of: std::false_type{};
template <typename Base, typename Derived>
struct is_virtual_base_of<Base, Derived, typename std::enable_if<
std::is_base_of<Base, Derived>::value &&
!can_static_cast<Base*, Derived*>::value
>::type>: std::true_type{};
struct Base{};
struct NonVirtual: Base{};
struct Virtual: virtual Base{};
static_assert(is_virtual_base_of<Base, NonVirtual>::value == false);
static_assert(is_virtual_base_of<Base, Virtual>::value == true);
在 Godbolt 上查看:https://godbolt.org/z/qnT6aq