Reputation: 33579
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.
Upvotes: 1
Views: 400
Reputation: 170055
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.
Upvotes: 4