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");
生活的例子。
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 usingstd::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 thatfoo
is not static, or the first overload would have been chosen):- If
T::foo
is valid (a member exists), it returnsstd::true_type
. - 如果T::foo是有效的(存在成员),则返回std:::true_type。
- Otherwize it falls back to the last overload and returns
std::false_type
. - 另一种方法是返回到最后一个重载并返回std::false_type。
- If
- 如果测试::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 sinceint
is an exact match whilelong
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 ifT::foo
is not valid, but the resulting type isstd::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_函数),也可以查看这两个答案:
- https://*.com/a/36694627/2666289
- https://*.com/a/36694627/2666289
- https://*.com/a/36693801/2666289
- https://*.com/a/36693801/2666289
#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 usingstd::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 thatfoo
is not static, or the first overload would have been chosen):- If
T::foo
is valid (a member exists), it returnsstd::true_type
. - 如果T::foo是有效的(存在成员),则返回std:::true_type。
- Otherwize it falls back to the last overload and returns
std::false_type
. - 另一种方法是返回到最后一个重载并返回std::false_type。
- If
- 如果测试::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 sinceint
is an exact match whilelong
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 ifT::foo
is not valid, but the resulting type isstd::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_函数),也可以查看这两个答案:
- https://*.com/a/36694627/2666289
- https://*.com/a/36694627/2666289
- https://*.com/a/36693801/2666289
- https://*.com/a/36693801/2666289