Checking a Member Exists, Possibly in a Base Class, C++11 Version

Checking a member exists, possibly in a base class, C++11 version

Actually, things got much easier in C++11 thanks to the decltype and late return bindings machinery.

Now, it's just simpler to use methods to test this:

// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
return true;
}

// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }

You can then use this in a class for example:

template <typename T, bool b>
struct Reserver {
static void apply(T& t, size_t n) { t.reserve(n); }
};

template <typename T>
struct Reserver <T, false> {
static void apply(T& t, size_t n) {}
};

And you use it so:

template <typename T>
bool reserve(T& t, size_t n) {
Reserver<T, has_reserve_method(t)>::apply(t, n);
return has_reserve_method(t);
}

Or you can choose a enable_if method:

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
t.reserve(n);
return true;
}

template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
return false;
}

Note that this switching things is actually not so easy. In general, it's much easier when just SFINAE exist -- and you just want to enable_if one method and not provide any fallback:

template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
t.reserve(n);
}

If substitution fails, this method is removed from the list of possible overloads.

Note: thanks to the semantics of , (the comma operator) you can chain multiple expressions in decltype and only the last actually decides the type. Handy to check multiple operations.

Checking a member exists, possibly in a base class, VS2005/08 version

I was able to come up with a solution that works in all major compilers. Sadly, there is a preprocessor check for MSVC because it complains about the solution for other compilers. The main difference is that MSVC does not accept function pointers inside sizeof() and conversely, GCC does not seem to accept (&C::resize == 0) in the check. Clang happily accepts both.

#include <iostream>

class Base {
public:
void resize(int, int, int) { }
};

class Derived : public Base {

};

class Unrelated { };

template<typename T>
class has_resize_method {
struct Yes { char unused[1]; };
struct No { char unused[2]; };

#ifdef _MSC_VER
template <class C>
static Yes test(char (*)[(&C::resize == 0) + 1]);
#else
template <class C>
static Yes test(char (*)[sizeof(&C::resize) + 1]);
#endif
template<class C>
static No test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(Yes));
};

int main() {
std::cout << (has_resize_method<Base>::value ? "Base has method resize" : "Base has NO method resize") << std::endl;
std::cout << (has_resize_method<Derived>::value ? "Derived has method resize" : "Derived has NO method resize") << std::endl;
std::cout << (has_resize_method<Unrelated>::value ? "Unrelated has method resize" : "Unrelated has NO method resize") << std::endl;
return 0;
}

Output:

Base has method resize
Derived has method resize
Unrelated has NO method resize

Tested on GCC 4.5.3, GCC 4.3.4, Clang 3.0, Visual C++ 2008 and Visual C++ 2010. I don't have access to Visual C++ 2005 but I think it will work there, too. It also compiles on Comeau Online but I cannot guarantee it produces a correct output there.

Works with both final and __sealed classes.

Note though that it checks not only for member functions but for member pointers in general. You might want to add additional checks such as boost::is_member_function_pointer if this behavior is unwanted. Similarly, you might want to add checks for number of arguments/argument types/result types - again, boost will be very helpful here, esp. boost type decomposition.

SFINAE to check for inherited member functions

Take a look at this thread:

http://lists.boost.org/boost-users/2009/01/44538.php

Derived from the code linked to in that discussion:

#include <iostream>

template <typename Type>
class has_foo
{
class yes { char m;};
class no { yes m[2];};
struct BaseMixin
{
void foo(){}
};
struct Base : public Type, public BaseMixin {};
template <typename T, T t> class Helper{};
template <typename U>
static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 0);
static yes deduce(...);
public:
static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0)));
};

struct A {
void foo();
};

struct B : A {};

struct C {};

int main()
{
using namespace std;
cout << boolalpha << has_foo<A>::result << endl;
cout << boolalpha << has_foo<B>::result << endl;
cout << boolalpha << has_foo<C>::result;
}

Result:

true
true
false

Templated check for the existence of a class member function?

Yes, with SFINAE you can check if a given class does provide a certain method. Here's the working code:

#include <iostream>

struct Hello
{
int helloworld() { return 0; }
};

struct Generic {};

// SFINAE test
template <typename T>
class has_helloworld
{
typedef char one;
struct two { char x[2]; };

template <typename C> static one test( decltype(&C::helloworld) ) ;
template <typename C> static two test(...);

public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

int main(int argc, char *argv[])
{
std::cout << has_helloworld<Hello>::value << std::endl;
std::cout << has_helloworld<Generic>::value << std::endl;
return 0;
}

I've just tested it with Linux and gcc 4.1/4.3. I don't know if it's portable to other platforms running different compilers.

How can I detect whether a type is a visible base of another type?

Do you have access to a compiler with C++11 support?

If so, you can combine Chad's use of static_cast with decltype to create a very simple type trait implementation (as demonstrated in this question). As per Jonathan Wakely's suggestion, the references have been replaced with pointers to avoid false positives when D defines an operator B&().

template<typename> struct AnyReturn { typedef void type; };

template<typename B, typename D, typename Sfinae = void>
struct is_base_of: std::false_type {};

template<typename B, typename D>
struct is_base_of<B, D,
typename AnyReturn< decltype( static_cast<B*>( std::declval<D*>() ) ) >::type
>: std::true_type {};

When using gcc 4.7:

struct Base {};
struct PublicDerived : public Base {};
struct PrivateDerived : private Base {};

int main()
{
std::cout << is_base_of<Base, PublicDerived >::value << std::endl; // prints 1
std::cout << is_base_of<Base, PrivateDerived>::value << std::endl; // prints 0
return 0;
}

Checking for existence of an (overloaded) member function

In C++ it impossible [so far] to take the address of an overload set: when you take the address of a function or a member function the function is either unique or it is necessary to have the appropriate pointer be chosen, e.g., by passing the pointer immediately to a suitable function or by casting it. Put differently, the expression &C::helloworld fails if helloworld isn't unique. As far as I know the result is that it is not possible to determine whether a possibly overloaded name is present as a class member or as a normal function.

Typically you'll need to do something with the name, however. That is, if it is sufficient to know if a function is present and can be called with a set of arguments of specified type, the question becomes a lot different: this question can be answered by attempting a corresponding call and determining its type in a SFINAE-able context, e.g.:

template <typename T, typename... Args>
class has_helloworld
{
template <typename C,
typename = decltype( std::declval<C>().helloworld(std::declval<Args>()...) )>
static std::true_type test(int);
template <typename C>
static std::false_type test(...);

public:
static constexpr bool value = decltype(test<T>(0))::value;
};

You'd then use this type to determine if there is a member which can suitably be called, e.g.:

std::cout << std::boolalpha
<< has_helloworld<Hello>::value << '\n' // false
<< has_helloworld<Hello, int>::value << '\n' // true
<< has_helloworld<Generic>::value << '\n'; // false

What am I doing wrong with enable_if and has_member?

Your hasEmpty::empty method returns bool:

struct 
{
bool empty() const
{
return true;
}
} hasEmpty;

But your trait uses a member function pointer that returns void, that substitution will always fail. You should change this:

template<typename C>
ctatic char f(check<void (C::*)() const, &C::empty> *);

For this:

template<typename C>
static char f(check<bool (C::*)() const, &C::empty> *);

That compiles for me.

How to check if a class is declared in C++?

You can, and with no macros required. First an observation, you can "forward" declare a class even after its full definition is available. I.e. this is valid:

class foo{};
class foo;

Now, with the help of a homebrew void_t implementation and an is_complete type utility, you can do something like this:

#include <type_traits>

template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;

template <typename T, typename Enabler = void>
struct is_complete : std::false_type {};

template <typename T>
struct is_complete<T, ::void_t<decltype(sizeof(T) != 0)>> : std::true_type {};

class A;
class B;

class C : public std::conditional<is_complete<A>::value, A, B>::type {
};

Depending on whether or not the full definition of A is present, C will inherit from A or B publicly. See a live example.

But I caution, this needs to be handled with care or you are very likely to have an ODR-violation in your program.



Related Topics



Leave a reply



Submit