如何检测class中是否真的存在特定的成员变量?
How to detect whether there is actually a specific member variable in class?
我故意使用与 this question 完全相同的标题,因为我觉得被接受的答案并不能解决我遇到的问题。
我正在寻找一种方法来检测某些 class 是否具有某些成员 变量 。重要的是要注意,我正在寻找一个 变量 ,而不是成员函数或其他任何东西。
这是我链接的问题中提供的示例:
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A { int x; };
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
但是如果我们做类似的事情,我们会得到完全相同的输出
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A {
void x()
{
}
};
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
(请注意,在第二个示例中,A
中的 int x
被替换为成员函数 void x()
)。
我不知道如何解决这个问题。我通过
之类的操作部分解决了这个问题
template <bool, typename> class my_helper_class;
template <typename ctype> class my_helper_class <true, ctype>
{
static bool const value = std :: is_member_object_pointer <decltype(&ctype :: x)> :: value;
};
template <typename ctype> class my_helper_class <false, ctype>
{
static bool const value = false;
};
template <typename T> struct HasX
{
// ...
static bool const value = my_helper_class <sizeof(f <Derived>(0)) == 2, T> :: value;
};
如果我使用 object,它实际上会选择。但是,如果我的 class.
中有更多同名 x
的重载函数,则上述方法不起作用
例如,如果我这样做
struct A
{
void x()
{
}
void x(int)
{
}
};
然后指针未成功解析,对 HasX <A>
的调用未编译。
我该怎么办?是否有任何解决方法或更简单的方法来完成此操作?
问题是 HasX
只检查 name x
是否存在。如果 &C::x
是 不明确的 (如果它在 Fallback
和 T
中都匹配,则会选择 ...
)。仅当 &C::x
正好是 Fallback::x
时才会选择 ChT<>
重载。在任何时候我们实际上都没有检查 T::x
的类型——所以我们从来没有真正检查 x
是否是变量或函数或其他任何东西。
解决方案是:使用 C++11 并检查 &T::x
是一个成员对象指针:
template <class T, class = void>
struct HasX
: std::false_type
{ };
template <class T>
struct HasX<T,
std::enable_if_t<
std::is_member_object_pointer<decltype(&T::x)>::value>
>
: std::true_type { };
如果 &T::x
不存在,替换失败,我们回退到主模板并得到 false_type
。如果 &T::x
存在但为重载名称,则替换失败。如果 &T::x
存在但为非重载函数,则 enable_if_t<false>
上的替换失败。 SFINAE 获胜。
这适用于所有这些类型:
struct A {
void x()
{
}
void x(int)
{
}
};
struct B { int X; };
struct C { int x; };
struct D { char x; };
int main() {
static_assert(!HasX<A>::value, "!");
static_assert(!HasX<B>::value, "!");
static_assert(HasX<C>::value, "!");
static_assert(HasX<D>::value, "!");
}
我故意使用与 this question 完全相同的标题,因为我觉得被接受的答案并不能解决我遇到的问题。
我正在寻找一种方法来检测某些 class 是否具有某些成员 变量 。重要的是要注意,我正在寻找一个 变量 ,而不是成员函数或其他任何东西。
这是我链接的问题中提供的示例:
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A { int x; };
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
但是如果我们做类似的事情,我们会得到完全相同的输出
template<typename T> struct HasX {
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
struct A {
void x()
{
}
};
struct B { int X; };
int main() {
std::cout << HasX<A>::value << std::endl; // 1
std::cout << HasX<B>::value << std::endl; // 0
}
(请注意,在第二个示例中,A
中的 int x
被替换为成员函数 void x()
)。
我不知道如何解决这个问题。我通过
之类的操作部分解决了这个问题template <bool, typename> class my_helper_class;
template <typename ctype> class my_helper_class <true, ctype>
{
static bool const value = std :: is_member_object_pointer <decltype(&ctype :: x)> :: value;
};
template <typename ctype> class my_helper_class <false, ctype>
{
static bool const value = false;
};
template <typename T> struct HasX
{
// ...
static bool const value = my_helper_class <sizeof(f <Derived>(0)) == 2, T> :: value;
};
如果我使用 object,它实际上会选择。但是,如果我的 class.
中有更多同名x
的重载函数,则上述方法不起作用
例如,如果我这样做
struct A
{
void x()
{
}
void x(int)
{
}
};
然后指针未成功解析,对 HasX <A>
的调用未编译。
我该怎么办?是否有任何解决方法或更简单的方法来完成此操作?
问题是 HasX
只检查 name x
是否存在。如果 &C::x
是 不明确的 (如果它在 Fallback
和 T
中都匹配,则会选择 ...
)。仅当 &C::x
正好是 Fallback::x
时才会选择 ChT<>
重载。在任何时候我们实际上都没有检查 T::x
的类型——所以我们从来没有真正检查 x
是否是变量或函数或其他任何东西。
解决方案是:使用 C++11 并检查 &T::x
是一个成员对象指针:
template <class T, class = void>
struct HasX
: std::false_type
{ };
template <class T>
struct HasX<T,
std::enable_if_t<
std::is_member_object_pointer<decltype(&T::x)>::value>
>
: std::true_type { };
如果 &T::x
不存在,替换失败,我们回退到主模板并得到 false_type
。如果 &T::x
存在但为重载名称,则替换失败。如果 &T::x
存在但为非重载函数,则 enable_if_t<false>
上的替换失败。 SFINAE 获胜。
这适用于所有这些类型:
struct A {
void x()
{
}
void x(int)
{
}
};
struct B { int X; };
struct C { int x; };
struct D { char x; };
int main() {
static_assert(!HasX<A>::value, "!");
static_assert(!HasX<B>::value, "!");
static_assert(HasX<C>::value, "!");
static_assert(HasX<D>::value, "!");
}