C++ 检查成员是否在类中声明

C++ 检查成员是否在类中声明,c++,sfinae,typetraits,c++17,C++,Sfinae,Typetraits,C++17,是否可以检查给定类中是否声明了成员变量、成员函数或类型定义 StackOverflow上的各种问题都涉及到检查给定类是否只包含一个成员,基本上使用std::is_进行检测。但所有这些解决方案都会在派生类中检测成员,而派生类可能不会声明成员本身 例如,以下内容不可编译 #include <experimental/type_traits> struct base { using type = std::true_type; }; struct derived : public

是否可以检查给定类中是否声明了成员变量、成员函数或类型定义

StackOverflow上的各种问题都涉及到检查给定类是否只包含一个成员,基本上使用std::is_进行检测。但所有这些解决方案都会在派生类中检测成员,而派生类可能不会声明成员本身

例如,以下内容不可编译

#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>);
}
#包括
结构基
{
使用type=std::true\u类型;
};
派生结构:公共基{};
模板
使用has\u type\u t=typename t::type;
模板
constexpr内联布尔具有\u类型\u v=
std::实验::是否检测到::值;
int main()
{
静态断言(具有类型v);
静态断言(!has_type_v);
}

是否可以进行任何更改以使这两个断言保持不变?还是需要反思?

我倾向于说不。证明这一点当然很难,但我可以解释为什么我这么认为

C++是一种编译语言。编译器具有类型的内部表示形式,您无法直接访问它。访问这种内部表示的唯一方法是通过语言的设施。实际实现在内部表示类型的方式上可能有所不同,并且通常有额外的信息来生成更好的错误消息。但这并没有暴露出来

因此,是的,大多数编译器都可以枚举基类型,并确切地知道类的每个成员来自何处。这对于良好的错误消息至关重要。但是,不能只使用C++模板在编译时枚举基类。 您可能认为,对于数据成员,可以尝试使用地址和偏移量的技巧。这是行不通的,因为您需要知道所有基类的大小,而不能枚举这些基类


您还可以考虑创建进一步派生的帮助类的技巧。这是可能的,但他们也有同样的问题。只能从给定类型派生,不能从其父类型派生。因此,可以创建子类,但不能创建兄弟类。而这个孩子继承自父母和祖父母。即使您在helper类中使用派生的::type编写了

,也会发现
base::type

,但是有一个限制,您的编译器必须有一个提供基类列表的内部函数(GCC提供了它)

如果您可以访问这样的en内在函数,唯一的困难是检查通过派生函数的成员访问实际上不是对基函数成员的访问

要检查这一点,我们需要使用在访问派生类的多个基中声明的成员时发生的“不明确”访问:

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>);
模板
多重结构:T,基{};
模板
结构的类型不明确
:std::true_type{};
模板
结构的类型不明确
:std::false_type{};
模板
结构具有\u类型
:std::false_type{};
模板
结构具有\u类型
:std::true_type{};
模板
constexpr内联自动已声明类型\u imp=
具有类型::值
//如果发生任何不明确的访问,则T具有“类型”
&&((具有不明确的类型::值| |……)
//或者没有出现歧义,因为没有一个基具有“type”
||(!has_type::value&&…);
模板
constexpr内联自动已声明类型=
_type_声明了_imp//本质的
静态断言(已声明类型);
静态断言(!已声明类型);

唯一的问题是可移植性:编译器必须提供访问类型的直接基列表的机制。在这里,我使用了GCC固有的
\u直接\u基

我看不到类型或静态成员的方法,但对于常规成员,您可以区分
基::m
派生::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;
模板
使用has\m\u t=decltype(&t::m);
模板
constexpr内联bool有=
标准::实验::是否检测到准确的::值;
以及测试:

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>);
struct base{int m;};
派生结构:公共基{};
不带{}的结构;
静态断言(has_m_v);
静态断言(!has_m_v);
静态断言(!has_m_v);

struct-derived:public-base{using-base::type;}的预期结果是什么?你的用途是什么?您可以检查成员Btw,但对于类型…@VTT,对于这种情况,我希望得到“true”。因此,我希望它在示例中表现为“base”,因为它声明了自己的成员。但我想,在这方面不同的解决方案也会很有趣/正确。@Jarod42我想标记某些类,这些类可以作为另一个模板的模板参数使用。我目前正在为此使用一个类型特征,但用户必须编写一些样板文件才能选择加入。如果他们能说“using is_allowed_for_use=std::true_type”或者类似于opt-in,那就更好了。因此,检查静态成员对我来说也一样好。我认为您所描述的内容需要某种形式的反射,除非我们能找到某种惯用方法在每个派生类中正确隐藏
类型
(但即使这样,它也不会是成员的“即插即用”解决方案),您可以知道:@Jarod42:看起来这是正确的答案,并且(总结)您的技巧是
&Derived::member
在继承时有一些类型
Base::*
。我可以建议你把它变成一个答案吗?向类中添加一个虚拟成员似乎不是解决OP问题的干净方法
type
或static member就可以了(但我看不出有什么办法可以检查)。我同意@Jarod42的回答对于原始问题是最有建设性的(因为这个问题也询问成员变量),尽管它并不能真正解决我的问题。因此,应选择它作为解决方案。除此之外,我认为这里的答案可能是最准确的,尽管它的结论有点伤感。有趣的想法!但我