检查成员是否在课堂上宣布

时间:2018-01-25 10:48:48

标签: c++ sfinae typetraits c++17

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

关于StackOverflow的各种问题谈论检查某个给定类是否仅仅包含成员,主要是使用std :: is_detected。但是所有这些解决方案也会在派生类中检测成员,这可能不会自己声明成员。

例如,以下内容无法编译。

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

是否可以进行任何更改以使两个断言成立?或者需要反思吗?

3 个答案:

答案 0 :(得分:1)

我没有找到类型或静态成员的方法,但对于普通成员,您可以区分base::mderived::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>);

Demo

答案 1 :(得分:0)

我倾向于拒绝。证明这当然很难,但我可以解释为什么我这么认为。

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

所以是的,大多数编译器都可以枚举基类型,并确切地知道每个类成员的来源。这对于良好的错误消息至关重要。但是,只能使用C ++模板在编译时枚举基类。

您可能认为对于数据成员,您可以尝试使用地址和偏移量的技巧。这不会起作用,因为你需要知道所有基类的大小 - 你不能枚举它们。

您可能还会考虑创建进一步派生的辅助类的技巧。这是可能的,但他们遇到了同样的问题。您只能从给定类型派生,而不是从其父派生成。因此可以创建一个子类,但不是兄弟。那个孩子继承了父母和祖父母。即使您在帮助程序类中编写了using derived::type,也会找到base::type

答案 2 :(得分:0)

您可以但有限制,您的编译器必须具有提供基类列表的内在函数(GCC提供它)。

如果您可以访问此类内在函数,唯一的困难是检查通过派生的成员访问权限是否实际上是对基础成员的访问权。

要检查这一点,我们的想法是使用访问在派生类的多个基础中声明的成员时发生的“模糊”访问:

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