简体   繁体   中英

How to check at compile time if a function is called

I'm trying to identify at compile-time whether a function is ever called. Specifically, I want to throw a static assertion failure if it is:

template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
  // this is the function I want to call
}

template <typename... T>
int Function(T...)
{
  // This function should never be called, instead I want
  // a compile-time failure if this is called, because it
  // means the above function wasn't successfully resolved.
}

The reason I want to do this is because failure to correctly call Function() with the correct conditions results in thousands of lines of compiler error messages, none of which are very helpful to anyone who is not intimately familiar with the code base.

The reason I don't want to place a static_assert in Function is because we have many of these functions, and we have the means instead to generate the Catch-all versions via macros, which would avoid unnecessary growth of the code-base while producing more helpful error messages.

Can this be done?

Based on the comments on your question, you don't want a static_assert here:

template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
  // this is the function I want to call
}

...but there's actually nothing wrong with a static_assert here :

template <typename... T>
struct dependent_false { static constexpr bool value = false; };

template <typename... T>
int Function(T...)
{
  static_assert(dependent_false<T...>::value, "you are passing the wrong arguments!");
}

As you correctly noted, a simple static_assert(false, "..."); would fail at template definition time. To get something that only fails at instantiation time, you need a dependent expression, and the dependent_false helper struct is an easy way to get something that will be type-dependent, will pretty much always be false , but cannot be assumed by the compiler to truly always be false : the compiler cannot rule out you adding partial specialisations to make dependent_false<...>::value true for some type.


Looking back at this old question, there may be a much simpler answer: mark the overload as deleted.

template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
    // this is the function I want to call
}

template <typename... T>
int Function(T...) = delete;

This is not exactly the same thing, since this allows a caller to check the well-formedness of eg Function(int, int) instead of forcing an error, but it's more readable, and generally you would want that exact behaviour of not getting an error unless the function is actually used, not merely referenced.

Just to extend a bit on the accepted answer:
We can additionally define the following

template <class... T>
inline constexpr bool dependant_false_v = dependant_false<T...>::value;

Usage is then:

template <typename... T>
int Function(T...)
{
  static_assert(dependent_false_v<T...>, "you are passing the wrong arguments!");
}

Which looks more clean IMO and is shorter to write.

Using C++17, we can even get rid of the SFINAE enable_if and merge everything in just one function via constexpr if :

template <typename T>
auto Function(T value)
{
  if constexpr(someCondition) {
    // Code for your function
  } else {
    static_assert(dependent_false_v<T>, "you are passing the wrong arguments!");
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM