8
\$\begingroup\$

I have written type traits for checking, if given class T has_member, then if it either has_non_static_function_member, has_static_function_member or has_non_function_member. These type traits don't take function arguments of the member nor return type to check signature validity (I will provide other type traits for this matter).

I want to know if it can be done somehow simpler, and if the current code doesn't have some logical errors. Personally, I think I might have overcomplicated the implementation.

Here is initial version:

#include <iostream>
#include <type_traits>

template<class, class = void>
struct has_member_xxx : std::false_type
{ };

template<class T>
struct has_member_xxx<
    T,
    std::void_t<
        decltype(&T::xxx)
        >
    > : std::true_type
{ };

template<class, class = void>
struct has_non_static_member_function_xxx : std::false_type
{ };

template<class T>
struct has_non_static_member_function_xxx<
    T,
    std::enable_if_t<
        std::is_member_function_pointer<
            decltype(&T::xxx)
        >::value>
    > : std::true_type
{ };

template<class, class = void>
struct has_static_member_function_xxx : std::false_type
{ };

template<class T>
struct has_static_member_function_xxx<
    T,
    std::enable_if_t<
        (!has_non_static_member_function_xxx<T>::value) &&
        std::is_function<typename std::remove_pointer<decltype(&T::xxx)>::type>::value
        >
    > : std::true_type
{ };

template<class, class = void>
struct has_non_function_member_xxx : std::false_type
{ };

template<class T>
struct has_non_function_member_xxx<
    T,
    std::enable_if_t<
        has_member_xxx<T>::value &&
        (!has_non_static_member_function_xxx<T>::value) &&
        (!has_static_member_function_xxx<T>::value)
        >
    > : std::true_type
{ };

struct A
{
    int xxx;
};

struct B 
{
    int xxx();
};

struct C 
{
    static int xxx();
};

int main()
{ 
    std::cout << std::boolalpha;
    std::cout << "has member xxx: " << std::endl;
    std::cout << "A: " << has_member_xxx<A>::value << std::endl;
    std::cout << "B: " << has_member_xxx<B>::value << std::endl;
    std::cout << "C: " << has_member_xxx<C>::value << std::endl << std::endl;

    std::cout << "has non-static member function xxx" << std::endl;
    std::cout << "A: " << has_non_static_member_function_xxx<A>::value << std::endl;
    std::cout << "B: " << has_non_static_member_function_xxx<B>::value << std::endl;
    std::cout << "C: " << has_non_static_member_function_xxx<C>::value << std::endl << std::endl;

    std::cout << "has static member function xxx" << std::endl;
    std::cout << "A: " << has_static_member_function_xxx<A>::value << std::endl;
    std::cout << "B: " << has_static_member_function_xxx<B>::value << std::endl;
    std::cout << "C: " << has_static_member_function_xxx<C>::value << std::endl << std::endl;

    std::cout << "has non-function member xxx" << std::endl;
    std::cout << "A: " << has_non_function_member_xxx<A>::value << std::endl;
    std::cout << "B: " << has_non_function_member_xxx<B>::value << std::endl;
    std::cout << "C: " << has_non_function_member_xxx<C>::value << std::endl << std::endl;
}

Code is also on Coliru for convenience.

\$\endgroup\$
4
\$\begingroup\$
  1. If xxx is a non-accessible data member (e.g. private) your code fails to compile with gcc. However this seems to be gcc bug. (stackoverflow question) Still if your code is supposed to be compilable with gcc you should consider this.

  2. The templates give false for non-accessible (e.g. private) members. That is usually good though, because someone using quering about a class should only see the public interfacce anyway. Still consider what the intended behavior of your templates should be.

  3. If xxx is of reference type, then your has_member_xxx will give false, because taking an address to member of reference type is not allowed.

  4. has_member_xxx is also false if xxx is a static data member with deleted or non-accessible operator&(), although this is a pathological case because I don't see a good reason to use operator&() in that way. You might want to take std::addressof instead to avoid such a case.

  5. has_member_xxx will also not detect member function templates, because the address of a function template cannot be taken. However that is probably expected behavior.

  6. The same issue also arises for overloaded member functions, though. The adress of overloaded function can also not be taken. This is probably not intended behavior.

If you could specify exactly what your templates are supposed to detect / not detect that would be helpful.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.