类型特征:检查引用成员变量是否为静态。

时间:2022-01-29 16:24:18

I would like to check if a member variable of a class is static or not. Using std::is_member_pointer works fine for all types except for reference members.

我想检查一个类的成员变量是否是静态的。is_member_pointer对于除引用成员之外的所有类型都适用。

#include <type_traits>

struct A {
  int foo;
};

struct B : A {};

struct C {
  static int foo;
};

struct D : C {
};

struct E {
  int &foo;
};

struct F {
  static int &foo;
};

static_assert(std::is_member_pointer<decltype(&A::foo)>::value, "No");
static_assert(std::is_member_pointer<decltype(&B::foo)>::value, "No");
static_assert(!std::is_member_pointer<decltype(&C::foo)>::value, "No");
static_assert(!std::is_member_pointer<decltype(&D::foo)>::value, "No");

// Fail to compile:
static_assert(std::is_member_pointer<decltype(&E::foo)>::value, "No");

static_assert(!std::is_member_pointer<decltype(&F::foo)>::value, "No");

Live example.

生活的例子。

I understand the error, that a pointer cannot point to a reference member. But how to avoid it and still distinguish if it is a static or non static variable? Any idea on that?

我理解错误,一个指针不能指向一个引用成员。但如何避免它,并仍然区分它是静态变量还是非静态变量?有什么想法吗?

1 个解决方案

#1


2  

You could add a fallback in case &E::foo fails using SFINAE (and another one in case E::foo does not exist at all):

您可以在使用SFINAE失败的情况下添加回退(而在E::foo的情况下则不存在):

template <typename T>
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int);

template <typename T>
decltype(T::foo, std::true_type{}) is_member_foo(long);

template <typename T>
std::false_type is_member_foo(...);

template <typename T>
using IsMemberFoo = decltype(is_member_foo<T>(0));

static_assert(IsMemberFoo<A>{}, "No");
static_assert(IsMemberFoo<B>{}, "No");
static_assert(!IsMemberFoo<C>{}, "No");
static_assert(!IsMemberFoo<D>{}, "No");
static_assert(IsMemberFoo<E>{}, "No");
static_assert(!IsMemberFoo<F>{}, "No");
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { };

What this code does:

什么这段代码:

  • If &T::foo is valid, it will check if the member is static or not using std::is_member_pointer (your version).
  • 如果&T:::foo是有效的,它将检查成员是否是静态的或不使用std::is_member_pointer(您的版本)。
  • If &T::foo is not valid, it falls back to the second overload (here you are sure that foo is not static, or the first overload would have been chosen):
    • If T::foo is valid (a member exists), it returns std::true_type.
    • 如果T::foo是有效的(存在成员),则返回std:::true_type。
    • Otherwize it falls back to the last overload and returns std::false_type.
    • 另一种方法是返回到最后一个重载并返回std::false_type。
  • 如果测试::foo无效,则返回到第二个重载(这里您确定foo不是静态的,或者第一个重载将被选中):如果T::foo是有效的(一个成员存在),它返回std::true_type。另一种方法是返回到最后一个重载并返回std::false_type。

Also note (thanks to @iammilind) that for private member, T::foo is not valid, so the third overload will be chosen.

还要注意(感谢@iammilind),对于私有成员,T::foo无效,因此将选择第三个重载。

Working example on ideone: http://ideone.com/FILHbK

关于ideone的工作示例:http://ideone.com/FILHbK

Side notes (extended explanation):

一边笔记(扩展解释):

  • When &T::foo is valid, the two first overloads are valid, but the first one is chosen since int is an exact match while long is not.
  • 当&T::foo是有效的,第两个重载是有效的,但是第一个重载是被选择的,因为int是一个精确的匹配,而long不是。
  • decltype(T::foo, std::true_type{}): T::foo is only here to "let SFINAE" fall back to the third overload if T::foo is not valid, but the resulting type is std::true_type thanks to the comma operator.
  • type(T::foo, std: true_type{}: T:::foo仅在这里是为了“让SFINAE”返回到第三个重载,如果T::foo无效,但是结果类型是std:::true_type,多亏了逗号操作符。

If you like, you can also create a generic version (http://ideone.com/lzH2FB):

如果您愿意,也可以创建一个通用版本(http://ideone.com/lzH2FB):

#define IsMember(MEM) \
template <typename T> \
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \
template<typename T> \
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \
template <typename T> \
std::false_type is_member_##MEM(...); \
template <typename T> \
using IsMember_##MEM = decltype(is_member_##MEM<T>(0));

// Instanciate IsMember_foo
IsMember(foo);

// Use it:
static_assert(IsMember_foo<A>{}, "No");

Also see these two answers if you want to encapsulate everything in a class (without having is_member_ functions):

如果您想要在类中封装所有内容(没有is_member_函数),也可以查看这两个答案:

#1


2  

You could add a fallback in case &E::foo fails using SFINAE (and another one in case E::foo does not exist at all):

您可以在使用SFINAE失败的情况下添加回退(而在E::foo的情况下则不存在):

template <typename T>
std::is_member_pointer<decltype(&T::foo)> is_member_foo(int);

template <typename T>
decltype(T::foo, std::true_type{}) is_member_foo(long);

template <typename T>
std::false_type is_member_foo(...);

template <typename T>
using IsMemberFoo = decltype(is_member_foo<T>(0));

static_assert(IsMemberFoo<A>{}, "No");
static_assert(IsMemberFoo<B>{}, "No");
static_assert(!IsMemberFoo<C>{}, "No");
static_assert(!IsMemberFoo<D>{}, "No");
static_assert(IsMemberFoo<E>{}, "No");
static_assert(!IsMemberFoo<F>{}, "No");
static_assert(!IsMemberFoo<G>{}, "No"); // struct G { };

What this code does:

什么这段代码:

  • If &T::foo is valid, it will check if the member is static or not using std::is_member_pointer (your version).
  • 如果&T:::foo是有效的,它将检查成员是否是静态的或不使用std::is_member_pointer(您的版本)。
  • If &T::foo is not valid, it falls back to the second overload (here you are sure that foo is not static, or the first overload would have been chosen):
    • If T::foo is valid (a member exists), it returns std::true_type.
    • 如果T::foo是有效的(存在成员),则返回std:::true_type。
    • Otherwize it falls back to the last overload and returns std::false_type.
    • 另一种方法是返回到最后一个重载并返回std::false_type。
  • 如果测试::foo无效,则返回到第二个重载(这里您确定foo不是静态的,或者第一个重载将被选中):如果T::foo是有效的(一个成员存在),它返回std::true_type。另一种方法是返回到最后一个重载并返回std::false_type。

Also note (thanks to @iammilind) that for private member, T::foo is not valid, so the third overload will be chosen.

还要注意(感谢@iammilind),对于私有成员,T::foo无效,因此将选择第三个重载。

Working example on ideone: http://ideone.com/FILHbK

关于ideone的工作示例:http://ideone.com/FILHbK

Side notes (extended explanation):

一边笔记(扩展解释):

  • When &T::foo is valid, the two first overloads are valid, but the first one is chosen since int is an exact match while long is not.
  • 当&T::foo是有效的,第两个重载是有效的,但是第一个重载是被选择的,因为int是一个精确的匹配,而long不是。
  • decltype(T::foo, std::true_type{}): T::foo is only here to "let SFINAE" fall back to the third overload if T::foo is not valid, but the resulting type is std::true_type thanks to the comma operator.
  • type(T::foo, std: true_type{}: T:::foo仅在这里是为了“让SFINAE”返回到第三个重载,如果T::foo无效,但是结果类型是std:::true_type,多亏了逗号操作符。

If you like, you can also create a generic version (http://ideone.com/lzH2FB):

如果您愿意,也可以创建一个通用版本(http://ideone.com/lzH2FB):

#define IsMember(MEM) \
template <typename T> \
std::is_member_pointer<decltype(&T::MEM)> is_member_##MEM(int); \
template<typename T> \
decltype(T::MEM, std::true_type{}) is_member_##MEM(long); \
template <typename T> \
std::false_type is_member_##MEM(...); \
template <typename T> \
using IsMember_##MEM = decltype(is_member_##MEM<T>(0));

// Instanciate IsMember_foo
IsMember(foo);

// Use it:
static_assert(IsMember_foo<A>{}, "No");

Also see these two answers if you want to encapsulate everything in a class (without having is_member_ functions):

如果您想要在类中封装所有内容(没有is_member_函数),也可以查看这两个答案: