1

The following code compiles in Clang and GCC, but fails in MSVC.

template <typename... FieldsSequence>
struct S {
    static constexpr bool checkIdUniqueness()
    {
        using IdType = int;
        constexpr IdType fieldIds[sizeof...(FieldsSequence)]{ 0 };
        for (size_t i = 0; i < std::size(fieldIds) - 1; ++i)
        {
            if (fieldIds[i] > fieldIds[i + 1])
            {
                constexpr auto tmp = fieldIds[i];
                fieldIds[i] = fieldIds[i + 1];
                fieldIds[i + 1] = tmp;
            }
        }

        return true;
    }
};

The error message is:

expression did not evaluate to a constant
note: failure was caused by a read of a variable outside its lifetime
note: see usage of 'i'

Is there a way to make this work with all the three compilers? Ultimately, I need to bubble-sort the array to assert at compile time that all the values are unique.

https://godbolt.org/z/9XbP6-

7
  • What's FieldsSequence? Commented Oct 13, 2019 at 18:53
  • Unrelated to your problem, but technically you don't sort (not even bubble) your array. Bubble sort needs multiple passes over the container to "bubble" up the sorted values, commonly implemented using nested loops. And regarding the sorting, can't you use std::sort? And regarding the uniqueness, can't you use sets? Commented Oct 13, 2019 at 18:53
  • @StoryTeller: An abstract template type pack, see godbolt link for complete compilable sample. Commented Oct 13, 2019 at 18:55
  • 1
    @VioletGiraffe You should know by now that questions should be self-contained, and that your minimal reproducible example needs enough context for us to understand it without going to external sites or asking about what symbols are. Please consider a refresh of the help pages, especially how to ask good questions, as well as this question checklist. Commented Oct 13, 2019 at 18:57
  • 1
    @Someprogrammerdude: std::sort is not constexpr until C++20, and std::set not even then (AFAIK). If my code will work, I don't see why a second inner loop won't. Resulting in compile-time bubble-sorting. And then a separate for loop to check for uniqueness. Commented Oct 13, 2019 at 18:57

1 Answer 1

4

You overused constexpr declarations. For one, if fieldIds is declared constexpr then it's also const, and you can't mutate it. As for tmp, because it's declared constexpr the initializer must be a constant expression, but it can't be one really.

The correct approach is to remove constexpr from those declarations:

template <typename... FieldsSequence>
struct S {
    static constexpr bool checkIdUniqueness()
    {
        using IdType = int;
        IdType fieldIds[sizeof...(FieldsSequence)]{ 0 };
        for (size_t i = 0; i < std::size(fieldIds) - 1; ++i)
        {
            if (fieldIds[i] > fieldIds[i + 1])
            {
                auto tmp = fieldIds[i];
                fieldIds[i] = fieldIds[i + 1];
                fieldIds[i + 1] = tmp;
            }
        }

        return true;
    }
};

The function as a whole can still be evaluated in a constant expression, but there is no extra requirement on those variables now that can interfere with their declaration or usage.

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

3 Comments

Makes sense, but I was unsure if constexpr actually implies const, as in immutable even at compile time. Seems like C++ is missing another keyword, something like compiletime, which would be used instead of constexpr and could be combined with const as needed. Thanks for setting this straight for me.
One thing I still don't get: why can't tmp be constexpr?
@VioletGiraffe - Because now fieldIds isn't. Before it couldn't be because i isn't a constant expression. That's just the rules of the abstract machine.

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.