5

I have the following code snippet:

#include <iostream>
#include <type_traits>
#include <algorithm>
#include <cstdint>
using T = double;

int main()
{
    f();
}
void f() {
    T x = 2;
    if constexpr(std::is_integral_v<T>)
    {
        std::cout << std::min(static_cast<int64_t>(2), x);
    } else {
    std::cout << std::min(1.0, x);
    }
}

The compiler is explaining that

<source>:15:57: error: no matching function for call to 'min(int64_t, T&)'

I thought it wouldn't be a problem because when T is a double, the first branch won't be instantiated. Apparently my understanding is wrong. Could someone help point out where my understanding goes wrong?

Boann
  • 48,794
  • 16
  • 117
  • 146
user122049
  • 397
  • 1
  • 11

3 Answers3

5

You need to make f() template, and T template parameter.

template <typename T>
void f() {
    T x = 2;
    if constexpr(std::is_integral_v<T>)
    {
        std::cout << std::min(static_cast<int64_t>(2), x);
    } else {
    std::cout << std::min(1.0, x);
    }
}

then

int main()
{
    f<double>();
}

For constexpr if:

(emphasis mine)

If a constexpr if statement appears inside a templated entity, and if condition is not value-dependent after instantiation, the discarded statement is not instantiated when the enclosing template is instantiated .

Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:

void f() {
    if constexpr(false) {
        int i = 0;
        int *p = i; // Error even though in discarded statement
    }
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

Outside of a template, the "false" branch of a constexpr if clause is discarded, not ignored. Thus, the code in such a branch must still be well-formed (and yours isn't, for the reason given).

From cppreference:

Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
0

std::min works with references. And both the arguments should be the same type. Since you're providing 2 different types, it cannot decide which one the argument type should be. You can work around that by explicitly specifying the type you like both the arguments to be converted:

std::min<double>(static_cast<int64_t>(2), x)

Be careful with dangling references.

The case when the failing branch of if constexpr doesn't matter is only in templates and f is not a template function.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    But this statement `std::min(static_cast(2), x)` is in the branch that fails the constexpr check. Why does it matter if the overload cannot be resolved? – user122049 Mar 08 '21 at 09:21
  • @user122049 Oh, you're misguided by that. The case when failing `constexpr` branch doesn't matter is only in templates, not plain functions. – Aykhan Hagverdili Mar 08 '21 at 09:22
  • `min` _is_ a template function. – user122049 Mar 08 '21 at 09:23
  • @user122049 We're talking about the function which contains the `if constexpr`. In your case that's `f`. Make `f` a template and your code will compile fine. – Aykhan Hagverdili Mar 08 '21 at 09:24
  • Do you know why constexpr doesn't work in plain functions? This seems to be quite surprising. – user122049 Mar 08 '21 at 09:26
  • @user122049 it makes more sense if you think about it. It's not dependent on any template arguments, so it can't be broken depending on the argument. If it's broken, it's always broken and you should either go fix your (broken) code or remove it. – Aykhan Hagverdili Mar 08 '21 at 09:52