saga
saga

Reputation: 2113

Literal value is not being considered a constant expression during template instantiation

This code is supposed to check if a float equals the half of an int at compile time using template meta-programming: #include

struct A {
    constexpr static bool value = true;
};

struct B {
    constexpr static bool value = false;
};

template<int i>
struct Meta {
    constexpr static int value = i/2;
    constexpr static bool func(float n) {
        return std::conditional_t<n==value,A,B>::value;
    }
};


int main( int argc, const char *argv[] ) {
    constexpr bool b = Meta<4>::func(2);
    std::cout << b << std::endl;
    return 0;
}

But it refuses to compile. The compiler is saying that n is not a constant expression:

test.cpp: In substitution of ‘template<bool _Cond, class _Iftrue, class _Iffalse> using conditional_t = typename std::conditional::type [with bool _Cond = (n == (float)2); _Iftrue = A; _Iffalse = B]’:
test.cpp:15:50:   required from ‘static constexpr int Meta<i>::func(float) [with int i = 4]’
test.cpp:21:36:   required from here
test.cpp:15:50: error: ‘n’ is not a constant expression
         return std::conditional_t<n==value,A,B>::value;
                                                  ^~~~~
test.cpp:15:50: note: in template argument for type ‘bool’ 
test.cpp: In function ‘int main(int, const char**)’:
test.cpp:21:36:   in constexpr expansion of ‘Meta<4>::func((float)2)’
test.cpp:21:38: error: constexpr call flows off the end of the function
     constexpr int b = Meta<4>::func(2);
                                      ^

What's the problem here? The value being passed to Meta::func is a literal value. It should be treated like a constant.

What I'm interested in is how can I perform different actions based on a value at compile time. It should be possible because all the input required to calculate the output is available at compile time.

I want to know how can I perform different actions(which might involve types) based on a value at compile time. It should be possible because all the input required to calculate the output is available at compile time.

Upvotes: 2

Views: 1867

Answers (1)

Aconcagua
Aconcagua

Reputation: 25546

Problem is that there is one single function generated that needs to be callable with both constant expressions (as your literals are) as well as with variably modified values (results of former calculations, for instance).

The constexpr attribute of the function guarantees that the function is evaluated at compile time, if it is possible because of all of the arguments being constant expressions. If any of them is not, the function serves as ordinary function evaluated at runtime. Even if you never use the function this way, it must still be able to handle the case, and thus function arguments in general (n) can never be used as constexpr and in consequence n == value not either; not matter if used in if constexpr or (after your edit) as a template argument.

Actually, however, the inner if constexpr is obsolete anyway: You feed a constexpr function with compile time constants, so the mentioned guarantee applies and your function will be evaluated at compile time, no matter if you use if constexpr or not, thus your function can be simplified to:

constexpr static bool func(float n)
{
    return n == value;
}

Be aware that if this did not come out as expected, you couldn't have assigned the result to a constexpr bool either...

Denoted in the comments to the questions already, but important enough for being hinted to again: Be aware, too, of the rounding problems of floating arithmetics and comparison of such values via exact equality!

Upvotes: 4

Related Questions