-2

I have no idea how the compiler decides what can be a constant and what can't. It's clear that the expression being assigned to the bool is constexpr, because it seems to have no problem returning idx - 1, so the compiler knows it's a constant expression. I don't get this:

#include <tuple>
#include <cstdint>
#include <cstdio>
#include <array>

template <typename type, typename ... among_Ts>
consteval auto index_of_type() -> uint64_t
{
    uint64_t idx = 0;

    // CANNOT BE CONSTEXPR EVEN THOUGH IT IS CONSTEXPR 
    // REMOVING constexpr MAKES IT COMPILE. AND THIS FUNCTION IS CONSTEVAL
    constexpr bool bFound = ((++idx, std::is_same_v<type, among_Ts>) || ...);

    return idx - 1;
}

template <typename type, typename ... var_types>
consteval auto index_of_tyoe_in_tuple(const std::tuple<var_types...>&) -> uint64_t
{
    return index_of_type<type, var_types...>();
}

int main(int argc, char* argv[])
{
    constexpr std::tuple<int, double, char> my_tuple;

    constexpr int idx = index_of_tyoe_in_tuple<double>(my_tuple);
}

Godbolt link.

I can't do static_assert(bFound) if I don't make bFound constexpr.

Edit: I can't even do:

if constexpr (idx > 20) to check that way. So it says that idx > 20 isn't a constexpr, and yet the return idx - 1 actually is consteval. Weird.

Edit: Oh my god even if I put it inside another function it still doesn't work:

template <typename type, typename ... among_Ts>
consteval auto index_of_type() -> uint64_t
{

    uint64_t idx = 0;

    static_assert(sizeof ... (among_Ts) > 1);




    auto lambda = [=] <typename type_, typename ... amont_Ts_>() consteval mutable -> uint64_t
    {
        ((++idx, std::is_same_v<type, among_Ts>) || ...);

        return idx;
    };
// STILL CAN'T BE CONSTEXPR WHY?????
    constexpr uint64_t idx_of_type = lambda.template operator() < type, among_Ts... > ();


    return idx_of_type - 1;


}
5
  • Isn't incrementing the same idx an unsequenced expression? If so, the line is UB and compiler has to reject it as constexpr. I'm not entirely sure since they are chained with operators with well defined precedence, so might not be the case. Commented Jun 8 at 9:18
  • This question is similar to: Will consteval allow using static_assert on function arguments?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Jun 8 at 9:22
  • I believe you might be misinterpreting how consteval works, possible duplicate of stackoverflow.com/questions/57226797/… Commented Jun 8 at 9:23
  • 1
    We really need a canonical question for this. Commented Jun 8 at 19:53
  • 1
    "I have no idea how the compiler decides what can be a constant and what can't." If you declare the variable static constexpr, or constexpr in namespace scope, then you have decided that it is evaluated at compile time. Commented Jun 9 at 15:59

2 Answers 2

3

how the compiler decides what can be a consexpr and what can't.

It depends of where it is used, not if the expression leads to a constant. For example, template parameter, initialization of constexpr variable require a constant expression.

Then to be a constant expression, there are several cases, in your case

A core constant expression is any expression whose evaluation would not evaluate any one of the following language constructs: [..] 18.) (since C++14) modification of an object, unless the object has non-volatile literal type and its lifetime began within the evaluation of the expression

uint64_t idx = 0;

// CANNOT BE CONSTEXPR EVEN as idx is modified, and its lifetime began above
constexpr bool bFound = ((++idx, std::is_same_v<type, among_Ts>) || ...);

For your static_assert, you might split your expression

template <typename type, typename ... among_Ts>
consteval auto index_of_type() -> uint64_t
{
    static_assert(sizeof ... (among_Ts) > 1);

    constexpr bool bFound = ((std::is_same_v<type, among_Ts>) || ...);
    static_assert(bFound);
    uint64_t idx = 0;
    static_cast<void>(((++idx, std::is_same_v<type, among_Ts>) || ...));

    return idx - 1;
}

Demo

Sign up to request clarification or add additional context in comments.

1 Comment

That might be simpler just to repeat the expression. I ended up doing it by wrapping the expression in another function as a hack. The value returned from that wrapped function could be constexpr.
1

Regular variables inside constexpr/consteval functions are not constexpr by default as you have found out. If they were, then you'd potentially be able to do to weird things like variables with 'dynamic' types. While it is in principle possible to implement something like that, given that everything is being evaluated at compile time anyway, it's very different semantics from regular C++, and so constexpr evaluation is not defined to work that way.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.