Possible to use type_traits / SFINAE to find if a class defines a member TYPE?

Viewed 7190

I have seen this question which allows one to check for the existence of a member function, but I'm trying to find out whether a class has a member type.

In the example below, both evaluate to "false", but I would like to find a way so that has_bar<foo1>::value evaluates to false, and has_bar<foo2>::value evaluates to true.

Is this possible?

#include <iostream>

struct foo1;
struct foo2 { typedef int bar; };

template <typename T>
class has_bar
{
    typedef char yes;
    typedef long no;

    template <typename C> static yes check( decltype(&C::bar) ) ;
    template <typename C> static no  check(...);
public:
    enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};

int main()
{
    std::cout << has_bar<foo1>::value << std::endl;
    std::cout << has_bar<foo2>::value << std::endl;
    return 0;
}

Edit: implementing a specialisation in response to the answers below:

...if you use C::bar in the target template, the template will be discarded automatically for types that don't have that nested type.

I have tried to do this, but am clearly missing something

#include <iostream>

struct foo1;
struct foo2 { typedef int bar; };

template <typename T, typename U = void>
struct target
{
    target()
    {
        std::cout << "default target" << std::endl;
    }
};

template<typename T>
struct target<T, typename T::bar>
{
    target()
    {
        std::cout << "specialized target" << std::endl;
    }
};

int main()
{
    target<foo1>();
    target<foo2>();
    return 0;
}
4 Answers

C++20 Update:

It is now much more easier to check whether a given type contains a specific type definition.

template<typename T>
concept has_bar = requires {
    typename  T::bar;
};

... so your example code evolves to this:

#include <iostream>

struct foo1;
struct foo2 { typedef int bar; };

template <typename T, typename U = void>
struct target
{
    target()
    {
        std::cout << "default target" << std::endl;
    }
};

template<typename T>
requires(has_bar<T>)
struct target<T>
{
    target()
    {
        std::cout << "specialized target" << std::endl;
    }
};

int main()
{
    target<foo1>();
    target<foo2>();
    return 0;
}

Example on gcc.godbolt: https://gcc.godbolt.org/z/a15G13

Related