user122049
user122049

Reputation: 433

Why does min still complain inside constexpr?

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?

Upvotes: 5

Views: 151

Answers (3)

Adrian Mole
Adrian Mole

Reputation: 51845

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.

Upvotes: 1

songyuanyao
songyuanyao

Reputation: 172934

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
    }
}

Upvotes: 5

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29985

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.

Upvotes: 0

Related Questions