Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Compile-time check that an overloaded function can be called with a certain type of argument

Tags:

c++

Consider an overloaded function

void f(int);
void f(long);
void f(double);
void f(MyClass);

And a method in a template class with unknown argument type

template <class T>
struct C {
  void method(T arg) { ... }
};

I want to check at compile time if there is a version of f which can take arg as an argument.

template <class T>
struct C {
  void method(T arg) { 
    if constexpr (CAN_BE_CALLED(f, arg)) {
      f(arg);
    } else {
      g();
    }
  }
};

Is it possible to do that? I tried this and this but compiler complained about unresolved overloaded function type.

like image 502
5r1gsOi1 Avatar asked Jul 18 '18 14:07

5r1gsOi1


People also ask

How does the compiler decide which overloaded function to call?

At compile time, the compiler chooses which overload to use based on the types and number of arguments passed in by the caller. If you call print(42.0) , then the void print(double d) function is invoked. If you call print("hello world") , then the void print(std::string) overload is invoked.

Does C allow overloaded functions?

This feature is present in most of the Object Oriented Languages such as C++ and Java. But C doesn't support this feature not because of OOP, but rather because the compiler doesn't support it (except you can use _Generic).

Is operator overloading compile time?

Operator overloading is a compile-time polymorphism.

How are function calls matched with overloaded functions?

The call is resolved for an overloaded subroutine by an instance of function through a process called Argument Matching also termed as the process of disambiguation.


2 Answers

You could use the detection idiom to build such a test

template<typename = void, typename... Args>
struct test : std::false_type {};

template<typename... Args>
struct test<std::void_t<decltype(f(std::declval<Args>()...))>, Args...>
    : std::true_type {};

template<typename... Args>
inline constexpr bool test_v = test<void, Args...>::value;

And use it as

template <class T>
struct C
{
    void method(T arg)
    { 
        if constexpr (test_v<T>)
            f(arg);
        else
            g();
    }
};

Live

Or alternatively

template<typename... Args>
using test_t = decltype(f(std::declval<Args>()...));

template<typename... Args>
inline constexpr auto test_v = std::experimental::is_detected_v<test_t, Args...>;
like image 118
Passer By Avatar answered Oct 13 '22 20:10

Passer By


You might do the following with SFINAE:

template <class T, typename Enabler = void>
struct C {
    void method(T arg) {
        g();
    }
};

template <class T>
struct C<T, std::void_t<decltype(f(std::declval<T>()))>> {
    void method(T arg) { 
        f(arg);
    }
};

Demo

like image 41
Jarod42 Avatar answered Oct 13 '22 18:10

Jarod42