Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Other ways of checking if a class has a certain member function

Let's check if

struct Thing {
    int foo(double, bool) {return 0;}
};

has the int foo(double, bool) member function during compile time. There are many ways of doing this, and most are just variations of others. Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here? I'm just trying to learn some new techniques with templates and SFINAE.

#include <iostream>
#include <type_traits>

// Using void_t (this includes using std::is_detected).
template <typename T>
using void_t = void;

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

template <typename T>
struct has_foo<T,
        void_t<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>
    > : std::true_type {};

// Using the ... default argument.
template <typename T>
struct hasfoo {
    template <typename U>
    static std::true_type test (decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*);  // or 'decltype(static_cast<int>(std::declval<U>().foo(double{}, bool{})))*' works fine too.

    template <typename>
    static std::false_type test (...);

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

// Overloads and trailing return types.
template <typename>
struct Helper : std::true_type {};

template <typename T>
auto helper(int) -> Helper<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>;

template <typename>
std::false_type helper(long);

template <typename T>
constexpr bool hasFoo() {return decltype(helper<T>(0))::value;}

// Comma operator (basically the same as the above).
template <typename T>
auto check(int) -> decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})), std::true_type{});

template <typename T>
std::false_type check(...);

template <typename T>
using HasFoo = decltype(check<T>(0));

// Member function pointer template parameter.
template <typename T>
struct Hasfoo {
    template <typename U, int(U::*)(double, bool)>
    struct Tag;

    template <typename U>
    static constexpr bool test (Tag<U, &U::foo>*) {return true;}

    template <typename>
    static constexpr bool test (...) {return false;}

    static constexpr bool value = test<T>(nullptr);
};

// Tests
struct Thing {
    int foo(double, bool) {return 0;}
};

int main() {
    static_assert (has_foo<Thing>::value, "");
    static_assert (hasfoo<Thing>::value, "");
    static_assert (hasFoo<Thing>(), "");
    static_assert (HasFoo<Thing>::value, "");
}

Edit: I just remembered an elegant and more general solution that Yakk gave to a different question quite a while ago (here is his actual typing, modified only to match the foo function):

namespace meta {
  namespace details {
    template<template<class...>class Z, class=void, class...Ts>
    struct can_apply : std::false_type {};
    template<template<class...>class Z, class...Ts>
    struct can_apply<Z, decltype((void)(std::declval<Z<Ts...>>())), Ts...>:
      std::true_type
    {};
  }
  template<template<class...>class Z, class...Ts>
  using can_apply = details::can_apply<Z,void,Ts...>;
}

template<class T>
using member_foo = decltype(static_cast<int(T::*)(double, bool)>(&T::foo));

template<class T>
using has_member_foo = meta::can_apply<member_foo, T>;
like image 910
prestokeys Avatar asked Mar 30 '16 21:03

prestokeys


People also ask

How do you find the member function of a class?

The way to access non-static members of a function is by using the member of object operator. For example, class SomeObject { public: SomeObject() {} void SayHello() { ... } int age = 10; }; // When calling members.

Which class can have member functions?

Class: A class in C++ is the building block that leads to Object-Oriented programming. It is a user-defined data type, which holds its own data members and member functions, which can be accessed and used by creating an instance of that class.

Can we define member function inside the class?

Member Function: It is a function that can be declared as members of a class. It is usually declared inside the class definition and works on data members of the same class. It can have access to private, public, and protected data members of the same class.

What are the three categories of member function of a class?

Following are the different types of Member functions: Simple functions. Static functions. Const functions.


1 Answers

Can someone think of a way that is vastly different (or at least fairly creative) than the 5 ways I mention here?

A fairly creative way to do that could be the one below.
It is based on the noexcept operator and a trivial using declaration (here named Type).
SFINAE and partial template specialization do the rest.

It follows a minimal, working example:

#include<type_traits>
#include<utility>

template<typename T, bool>
using Type = T;

template<typename T, typename = T>
struct U: std::false_type {};

template<typename T>
struct U<T, Type<T, noexcept(std::declval<T>().f(42))>>: std::true_type {};

struct S { void f(int) {} };
struct T {};

int main() {
    static_assert(U<S>::value, "!");
    static_assert(not U<T>::value, "!");
}

This solution has a problem if compared to the others.
As an example, the struct below would pass the test as well:

struct R { int f(double) {} };

In other terms, as long as the function to be tested accepts one argument to the type of which 42 can be cast and no matter what's the return type, it is accepted.

like image 71
skypjack Avatar answered Oct 02 '22 14:10

skypjack