0

Coming from this question, how can we detect (at compile time) if a function/operator/method having certain signature is defined?

From linked question and looking at cppreference about std::void_t we can write (C++17 ahead)

#include<iostream>
#include<type_traits>
#include<utility>

class X { public: int someFunc() const{ return 9; } };
class Y{};

template<typename, typename = std::void_t<>> struct Has_someFunc
    : std::false_type{};
template<typename T> struct Has_someFunc<T, std::void_t<decltype(std::declval<T>().someFunc())>>
    : std::true_type{};

template<typename T>
void f(const T& v){
    if constexpr(Has_someFunc<T>::value)
        std::cout << "has someFunc()\n";
    else
        std::cout << "has NOT someFunc()\n";
}

int main()
{
    std::cout << "X "; f(X{});
    std::cout << "Y "; f(Y{});

    return 0;
}

which prints

X has someFunc()
Y has NOT someFunc()

but what if we want to test a type to have someFunc not only to be defined but also having a certain signature?

Though I'm using C++17 also answers in any other version of the standard are welcome.

user1832484
  • 371
  • 3
  • 8
  • Do you want to know if a `someFunc()` is invocable with a given signature (this is simple, starting from your code) or if exist a `someFunc()` with exactly a given signature? In second case: what about if there is a template `someFunc()` that is compatible with the given signature? – max66 Mar 01 '20 at 19:53
  • For my current need is enough your first statement, i.e. if I can call the method using arguments which are convertible from the ones I have and then get back a result which is convertible to the type I need. But I would like also to know how to be more strict about types. – user1832484 Mar 01 '20 at 20:08
  • Added an answer for the first case: a simple modification of your code. If you want to be more strict (check an exact signature)... maybe you should explain better what do you exactly want. – max66 Mar 01 '20 at 20:30

1 Answers1

1

If you intend check if a type has a method someFunc() that is compatible (in-vocable with) a given signature) and return a given type, you can modify your code as follows

#include<iostream>
#include<type_traits>
#include<utility>

class X
 { public: int someFunc (int, long) const { return 9; } };

class Y
 { };

template <typename, typename = void>
struct Has_someFunc : public std::false_type
 { };

template <typename T, typename RType, typename ... Args>
struct Has_someFunc<std::tuple<T, RType, Args...>, std::enable_if_t<
   std::is_same_v<RType, 
      decltype(std::declval<T>().someFunc(std::declval<Args>()...))>>>
    : public std::true_type
 { };

template <typename RType, typename ... Args, typename T>
void f (T const & v)
 {
   if constexpr (Has_someFunc<std::tuple<T, RType, Args...>>::value)
      std::cout << "has someFunc()\n";
   else
      std::cout << "has NOT someFunc()\n";
 }

int main()
{
    std::cout << "X "; f<int>(X{});
    std::cout << "X "; f<int, int, long>(X{});
    std::cout << "X "; f<int, int, int>(X{});
    std::cout << "Y "; f<int>(Y{});
}

Drawback: you get "has someFunc()" also from third invocation because the last argument (a int) is compatible with the last expected argument (a long).

max66
  • 65,235
  • 10
  • 71
  • 111