Search code examples
c++type-traitsstatic-assertif-constexpr

Best way to trigger a compile-time error if no if-constexpr's succeed?


I have a long series of if constexpr statements and would like to trigger a compile-time error if none of them succeed.

Specifically, I have an abstract syntax tree whose result I would like to convert to a specific set of types that I might need. I have AsInt(), AsDouble(), etc. working, but I need to be able to do so more dynamically based on a supplied type.

As things stand, I've written a templated As() member function, but it's error-checking is clunky. Specifically, using static assert requires an unwieldy test condition. Here's a simplified version:

template <typename T>
T As() {
  if constexpr (std::is_same_v<T,int>) return AsInt();
  if constexpr (std::is_same_v<T,double>) return AsDouble();
  ...
  if constexpr (std::is_same_v<T,std::string>) return AsString();

  static_assert( std::is_same_v<T,int>
                 || std::is_same_v<T,double>
                 || ...
                 || std::is_same_v<T,std::string>,
                 "Invalid template type for As()" );
}

Is there a simpler way to trigger the static assert (or equivalent) if all of the conditions fail?


Solution

  • You need to rewrite your sequence of if constexprs as a chain of if constexpr ... else if constexpr ... and have the final else clause trigger a compilation error if "reached" (i.e., not discarded). This can be done using the "dependent false idiom":

    if constexpr (std::is_same_v<T,int>) {
        return AsInt();
    } else if constexpr (std::is_same_v<T,double>) {
        return AsDouble();
    } ... else if constexpr (std::is_same_v<T,std::string>) {
        return AsString();
    } else {
        // this is still constexpr, so it will be discarded if any other branch was taken
        static_assert(dependent_false<T>::value, "invalid template type for As()");
    }
    

    where dependent_false is defined as:

    template <class T> struct dependent_false : std::false_type {};