5

I have a template function that checks the type of a template argument with an if constexpr such as

template <typename T>
bool something(T arg) {
  if constexpr (std::is_integral_v<T>) {
    return true;
  } else {
    // What can I write here so that something<double>(0.0) does not compile?
  }

  return false;
}

How can I make the code fail to compile if none of my if constexprs match?

Marten
  • 1,336
  • 10
  • 16
  • 2
    Add an static assert – Oblivion Jun 05 '19 at 18:44
  • 3
    `if constexpr` doesn't make sense here. `static_assert(std::is_integral_v)` seems like it would be a simpler approach. – Miles Budnek Jun 05 '19 at 18:45
  • 2
    @MilesBudnek *"if none of my if constexprs match"* This implies OP has several `if constexpr`s chained in their actual code. – HolyBlackCat Jun 05 '19 at 18:48
  • Having multiple type-checks like that is not considered good code, and could even be a sign of bad design. Perhaps you should consider overloading or specialization instead? – Some programmer dude Jun 05 '19 at 18:49
  • 1
    @Some How is it considered not good code? By whom? This is just another way of writing a series of template specialisations for a fixed set of types. You could even argue this is a more natural expression of the concept (it's not entirely equivalent though). – rubenvb Jun 05 '19 at 19:03
  • @rubenvb It can quickly become a very tangled mess that is hard to read, understand and maintain. In C++ there are cleaner and simpler solutions to overcome or work around the lack of introspection in the language. – Some programmer dude Jun 05 '19 at 19:34
  • @Oblivion https://stackoverflow.com/questions/38304847/constexpr-if-and-static-assert – vrqq Jul 09 '21 at 12:30
  • It seems that cannot use static_assert and if constexpr simultaneously – vrqq Jul 09 '21 at 12:31

3 Answers3

16

The soltuion is to use static_assert.

But we can't simply do static_assert(false, "whatever"); in the else branch, because since the condition doesn't depend on the template parameter, assertion might fire early (when the compiler first sees your function body, even if the else branch is never actually taken).

The condition of static_assert has to somehow depend on T, to delay the assertion check until your template is instantinated.

This is what I've been using:

template <auto A, typename...> auto value = A;
if constexpr (foo)
{
    ...
}
else if constexpr (bar)
{
    ...
}
else
{
    static_assert(value<false, T>, "Invalid template parameter.");
}

Note that if you only have one if constexpr (rather than an if else if chain), then none of that is needed.

Simply move the condition from if to a static_assert and remove the if.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • There seems a problem at: https://stackoverflow.com/questions/38304847/constexpr-if-and-static-assert So why the code in that weblink wrong? – vrqq Jul 09 '21 at 12:32
  • @vrqq That code creates an `if constexpr` branch that **can't** be valid, ever, making it ill-formed NDR. But in my code, the last branch can be made valid by specializing `value` to be true. – HolyBlackCat Jul 09 '21 at 17:03
1

Just to add another answer, although the idea is the same.

You can just use this in your else branch:

static_assert(!std::is_same_v<T,T>, "")

Kinda less readable but hey it works :)

Moshe Rabaev
  • 1,892
  • 16
  • 31
  • I think technically this is ill-formed NDR, exactly like `static_assert(false)`, because you're not allowed to specialize `is_same_v` to be true. If you were to write your own class, an exact copy of `std::is_same_v`, then it would become legal, because your own classes *can* be specialized. – HolyBlackCat Jul 09 '21 at 17:07
1

I had the same question.

I'm not an expert, but this worked for me and it doesn't require any extra definitions.

static_assert(! std::is_void<std::void_t<T>>::value , "")

First, std::void_t<T> always returns void type for any T. Then, std::is_void<T>::value is true if T is void. So this is a way to get true/false from any type T.

Should work from C++17 onward.