检查成员是否在 class 中声明
Check if member is declared in class
是否可以检查成员变量、成员函数或类型定义是否在给定的class中声明?
Whosebug 上的各种问题讨论了检查给定的 class 是否仅 包含 成员,本质上是使用 std::is_detected。但是所有这些解决方案也在派生的 classes 中检测成员,它们可能不会自己声明成员。
例如,以下不编译。
#include <experimental/type_traits>
struct base
{
using type = std::true_type;
};
struct derived : public base { };
template<typename T>
using has_type_t = typename T::type;
template<typename T>
constexpr inline bool has_type_v =
std::experimental::is_detected<has_type_t, T>::value;
int main ()
{
static_assert (has_type_v<base>);
static_assert (!has_type_v<derived>);
}
是否可以进行任何更改以使这两个断言成立?还是需要反思?
我倾向于拒绝。证明这一点当然很难,但我可以解释为什么我这么认为。
C++ 是一种编译语言。编译器有类型的内部表示,您不能直接访问它。访问此内部表示的唯一方法是通过语言的工具。实际实现在内部表示类型的方式上可能有所不同,并且通常确实有额外的信息来产生更好的错误消息。但是这个不暴露。
所以是的,大多数编译器都可以枚举基类型,并且确切地知道 class 的每个成员来自哪里。这对于良好的错误消息至关重要。但是你不能在编译时枚举基 classes,只使用 C++ 模板。
您可能认为对于数据成员,您可以尝试使用地址和偏移量的技巧。这同样行不通,因为您需要知道所有基数 classes 的大小 - 而您不能枚举它们。
您还可以考虑创建 further-derived 帮助程序 classes 的技巧。这是可能的,但他们遇到了同样的问题。您只能从给定类型派生,而不能从其 parent(s) 派生。因此可以创建 child class,但不能创建同级。 child 继承自 parent 和 grandparent。即使你在助手 class 中写了 using derived::type
,它也会找到 base::type
.
你可以,但有一个限制,你的编译器必须有一个内在函数来提供基 classes 的列表(GCC 提供它)。
如果您可以访问此类内部函数,唯一的困难是检查通过派生的成员访问实际上不是对基成员的访问。
为了检查这一点,我们的想法是使用 'ambiguous' 访问,这种访问发生在访问在派生 class 的多个基中声明的成员时:
struct base
{
using type = std::true_type;
};
struct Derived1 : base{
};
struct Derived2 : base{
using type = std::true_type;
};
struct Test1: Derived1,base{};
struct Test2: Derived2,base{};
void g(){
Test1::type a;
Test2::type b;//Do not compile: 'type' is ambiguous
}
所以你可以这样概括这个技巧:
template<class T,class Base>
struct MultiDer: T,Base{};
template<class T,class Base,class=void>
struct has_ambiguous_type
:std::true_type{};
template<class T,class Base>
struct has_ambiguous_type
<T,Base,std::void_t<typename MultiDer<T,Base>::type>>
:std::false_type{};
template<class T,class=void>
struct has_type
:std::false_type{};
template<class T>
struct has_type
<T,std::void_t<typename T::type>>
:std::true_type{};
template<class T,class...Bases>
constexpr inline auto has_type_declared_imp =
has_type<T>::value
//if any ambiguous access happens then T has 'type'
&& ( (has_ambiguous_type<T,Bases>::value || ...)
//or no ambiguity happened because none of the base has 'type'
|| (!has_type<Bases>::value && ...));
template<class T>
constexpr inline auto has_type_declared =
has_type_declared_imp<T,__direct_bases(T)...>;//GCC instrinsic
static_assert(has_type_declared<Derived2>);
static_assert(!has_type_declared<Derived1>);
唯一的问题是可移植性:您的编译器必须提供一种机制来访问类型的直接基列表。这里我使用了 GCC 的内在 __direct_bases
.
我看不到类型或静态成员的方法,但对于普通成员,您可以区分 base::m
和 derived::m
:
template<typename T>
using has_m_t = decltype(&T::m);
template<typename T>
constexpr inline bool has_m_v =
std::experimental::is_detected_exact<int (T::*), has_m_t, T>::value;
测试:
struct base { int m; };
struct derived : public base {};
struct without {};
static_assert( has_m_v<base>);
static_assert(!has_m_v<derived>);
static_assert(!has_m_v<without>);
是否可以检查成员变量、成员函数或类型定义是否在给定的class中声明?
Whosebug 上的各种问题讨论了检查给定的 class 是否仅 包含 成员,本质上是使用 std::is_detected。但是所有这些解决方案也在派生的 classes 中检测成员,它们可能不会自己声明成员。
例如,以下不编译。
#include <experimental/type_traits>
struct base
{
using type = std::true_type;
};
struct derived : public base { };
template<typename T>
using has_type_t = typename T::type;
template<typename T>
constexpr inline bool has_type_v =
std::experimental::is_detected<has_type_t, T>::value;
int main ()
{
static_assert (has_type_v<base>);
static_assert (!has_type_v<derived>);
}
是否可以进行任何更改以使这两个断言成立?还是需要反思?
我倾向于拒绝。证明这一点当然很难,但我可以解释为什么我这么认为。
C++ 是一种编译语言。编译器有类型的内部表示,您不能直接访问它。访问此内部表示的唯一方法是通过语言的工具。实际实现在内部表示类型的方式上可能有所不同,并且通常确实有额外的信息来产生更好的错误消息。但是这个不暴露。
所以是的,大多数编译器都可以枚举基类型,并且确切地知道 class 的每个成员来自哪里。这对于良好的错误消息至关重要。但是你不能在编译时枚举基 classes,只使用 C++ 模板。
您可能认为对于数据成员,您可以尝试使用地址和偏移量的技巧。这同样行不通,因为您需要知道所有基数 classes 的大小 - 而您不能枚举它们。
您还可以考虑创建 further-derived 帮助程序 classes 的技巧。这是可能的,但他们遇到了同样的问题。您只能从给定类型派生,而不能从其 parent(s) 派生。因此可以创建 child class,但不能创建同级。 child 继承自 parent 和 grandparent。即使你在助手 class 中写了 using derived::type
,它也会找到 base::type
.
你可以,但有一个限制,你的编译器必须有一个内在函数来提供基 classes 的列表(GCC 提供它)。
如果您可以访问此类内部函数,唯一的困难是检查通过派生的成员访问实际上不是对基成员的访问。
为了检查这一点,我们的想法是使用 'ambiguous' 访问,这种访问发生在访问在派生 class 的多个基中声明的成员时:
struct base
{
using type = std::true_type;
};
struct Derived1 : base{
};
struct Derived2 : base{
using type = std::true_type;
};
struct Test1: Derived1,base{};
struct Test2: Derived2,base{};
void g(){
Test1::type a;
Test2::type b;//Do not compile: 'type' is ambiguous
}
所以你可以这样概括这个技巧:
template<class T,class Base>
struct MultiDer: T,Base{};
template<class T,class Base,class=void>
struct has_ambiguous_type
:std::true_type{};
template<class T,class Base>
struct has_ambiguous_type
<T,Base,std::void_t<typename MultiDer<T,Base>::type>>
:std::false_type{};
template<class T,class=void>
struct has_type
:std::false_type{};
template<class T>
struct has_type
<T,std::void_t<typename T::type>>
:std::true_type{};
template<class T,class...Bases>
constexpr inline auto has_type_declared_imp =
has_type<T>::value
//if any ambiguous access happens then T has 'type'
&& ( (has_ambiguous_type<T,Bases>::value || ...)
//or no ambiguity happened because none of the base has 'type'
|| (!has_type<Bases>::value && ...));
template<class T>
constexpr inline auto has_type_declared =
has_type_declared_imp<T,__direct_bases(T)...>;//GCC instrinsic
static_assert(has_type_declared<Derived2>);
static_assert(!has_type_declared<Derived1>);
唯一的问题是可移植性:您的编译器必须提供一种机制来访问类型的直接基列表。这里我使用了 GCC 的内在 __direct_bases
.
我看不到类型或静态成员的方法,但对于普通成员,您可以区分 base::m
和 derived::m
:
template<typename T>
using has_m_t = decltype(&T::m);
template<typename T>
constexpr inline bool has_m_v =
std::experimental::is_detected_exact<int (T::*), has_m_t, T>::value;
测试:
struct base { int m; };
struct derived : public base {};
struct without {};
static_assert( has_m_v<base>);
static_assert(!has_m_v<derived>);
static_assert(!has_m_v<without>);