1

Similarily to this question is it possible to use SFINAE to determine if a type has a member function with a certain argument(s)? Without the argument list as answered in the other question and the std::void_t example says it works fine. (I stood with the latter.) However, If I try to add a check for argument lust with extra std::decltype<>() it fails with template parameters not deducible in partial specialization. Is there any way to extend this method to check for certain argument types?

Example code:


#include <type_traits>

class A {
public :
    void a(){}
    void b(int val){}
};

class B {
public :
    void b(float val){}
};

// --- Has function a() without arguments

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

template <typename T> struct has_a<T, std::void_t<decltype(std::declval<T>().a())>> : std::true_type
{
};

template <typename T> constexpr bool has_a_v = has_a<T>::value;

// This is OK:
static_assert(true == has_a_v<A>); 
static_assert(false == has_a_v<B>);

// --- Has function b() with one argument of one given type

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

template <typename T ,typename U> struct has_b<T, std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};

template <typename T, typename U> constexpr bool has_b_v = has_b<T, U>::value;

// This fails with `template parameters not deducible in partial specialization`:
static_assert(true == has_b_v<A, int>);
static_assert(false == has_b_v<B, int>);

static_assert(false == has_b_v<A, float>);
static_assert(true == has_b_v<B, float>);

int main () { return 0;}
Caiwan
  • 73
  • 1
  • 9
  • 1
    You have a typo. `std::declval().b(std::declval(U))` should be `std::declval().b( std::declval())` – Simon Kraemer Jun 24 '20 at 12:01
  • @Caiwan You have another typo - partial specialization of `has_b` needs to have three template parameters: ``. – jrok Jun 24 '20 at 17:51

2 Answers2

2

Yes. Example of a void_t check for member function b in class B:

decltype( static_cast<void(B::*)(float)>(&B::b) )

This is if you want to check for exact signature. Your way is fine, too (once you fix it as commented under the question), but it actually checks if member function is callable with certain types of arguments (and ignores the return type).

jrok
  • 54,456
  • 9
  • 109
  • 141
2

In your partial specialization, you forgot to add the U parameter:

template <typename T, typename U>
struct has_b<
    T,
    U, // You forgot it
    std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};
Rerito
  • 5,886
  • 21
  • 47