Как проверить, существует ли имя члена (переменная или функция) в классе, с указанием типа или без него?
Этот Q является расширением:
Можно ли написать шаблон C++ для проверки существования функции?
Есть ли какая-нибудь утилита, которая поможет найти:
- Если имя члена существует внутри класса или нет? Этот член может быть переменной или методом.
- Указание типа члена должно быть необязательным
3 ответа
C++03
#define HasMember(NAME) \
template<class Class, typename Type = void> \
struct HasMember_##NAME \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(static_cast<Type>(&V::NAME))>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}; \
template<class Class> \
struct HasMember_##NAME<Class, void> \
{ \
typedef char (&yes)[2]; \
template<unsigned long> struct exists; \
template<typename V> static yes Check (exists<sizeof(&V::NAME)>*); \
template<typename> static char Check (...); \
static const bool value = (sizeof(Check<Class>(0)) == sizeof(yes)); \
}
Использование: Просто вызовите макрос с любым членом, которого вы хотите найти:
HasMember(Foo); // Creates a SFINAE `class HasMember_Foo`
HasMember(i); // Creates a SFINAE `class HasMember_i`
Теперь мы можем использовать HasMember_X
Проверять X
в любой class
как показано ниже:
#include<iostream>
struct S
{
void Foo () const {}
// void Foo () {} // If uncommented then type should be mentioned in `HasMember_Foo`
int i;
};
int main ()
{
std::cout << HasMember_Foo<S, void (S::*) () const>::value << "\n";
std::cout << HasMember_Foo<S>::value << "\n";
std::cout << HasMember_i<S, int (S::*)>::value << "\n";
std::cout << HasMember_i<S>::value << "\n";
}
Ловит:
- В случае методов, если мы не упоминаем тип, то
class
не должно быть перегруженных методов. Если это так, то этот трюк не удался. т.е. даже если именованный элемент присутствует более одного раза, результат будетfalse
, - Если член является частью базового класса, этот трюк не выполняется; например, если
B
является основойS
&void B::Bar ()
присутствует, тогдаHasMember_Bar<S, void (B::*)()>::value
или жеHasMember_Bar<S, void (S::*)()>::value
или жеHasMember_Bar<S>::value
дамfalse
С std::experimental::is_detected
а также std::experimental::disjunction
Вы могли бы сделать это:
//check for a type member named foo
template <typename T>
using foo_type_t = typename T::foo;
//check for a non-type member named foo
template <typename T>
using foo_non_type_t = decltype(&T::foo);
template <typename T>
using has_foo = disjunction<is_detected<foo_type_t, T>,
is_detected<foo_non_type_t, T>>;
Тогда вы бы использовали has_foo<my_class>::value
во что угодно.
Вышеописанное будет работать не только для типов и функций-членов, но вы можете легко ограничить это, используя такие черты, как std::is_member_function_pointer
а также std::is_member_object_pointer
если хочешь.
Чтобы указать необязательный аргумент, вы можете использовать std::experimental::is_detected_exact
помощник.
Обратите внимание, что если вы берете реализации вышеупомянутых черт со страниц, на которые я ссылаюсь, вы можете использовать это с C++14. Незначительное изменение в disjunction
код позволит вам использовать его в C++11.
Для не-C++17:
В библиотеке Boost Type Traits есть метафункции как для проверки наличия члена с заданным именем, так и для более точного контроля с помощью подписи.
Ниже приведена программа, использующая слегка модифицированныеHAS_MEMBER
макрос:
#include <iostream>
#include <type_traits>
#define DECLARE_HAS_MEMBER(__trait_name__, __member_name__) \
\
template <typename __boost_has_member_T__> \
class __trait_name__ \
{ \
using check_type = ::std::remove_const_t<__boost_has_member_T__>; \
struct no_type {char x[2];}; \
using yes_type = char; \
\
struct base { void __member_name__() {}}; \
struct mixin : public base, public check_type {}; \
\
template <void (base::*)()> struct aux {}; \
\
template <typename U> static no_type test(aux<&U::__member_name__>*); \
template <typename U> static yes_type test(...); \
\
public: \
\
static constexpr bool value = (sizeof(yes_type) == sizeof(test<mixin>(0))); \
}
struct foo
{
int bar(){}
};
struct baz
{};
DECLARE_HAS_MEMBER(has_bar, bar);
int main()
{
std::cout << has_bar<foo>::value << '\n' << has_bar<baz>::value;
}
Как отмечено на связанной странице, он использует тот факт, что если вы наследуете от двух классов, имеющих членов с одинаковыми именами, попытки использовать это имя станут неоднозначными.