I am trying to use the return value from a consteval function UniqueSize()
inside another consteval function UniqueArra
to declare an array, but this results in a compiler error saying that UniqueSize()
is not a constant expression.
However, if I use UniqueSize()
inside main()
and use its return value to declare an array the compiler just accepts it.
Why is this happening? Can someone please explain the difference of constant expressions in these contexts?
See the code below. https://godbolt.org/z/nd18WE8M5
template <typename... Args>
consteval auto UniqueSize(Args&&... args)
{
std::vector v{std::forward<Args>(args)...};
std::sort(v.begin(), v.end());
auto newEnd = std::unique(v.begin(), v.end());
return std::distance(v.begin(), newEnd);
}
template <typename... Args>
consteval auto UniqueArray(Args&&... args)
{
// Calling UniqueSize to determine the size of the array returned
constexpr auto sz = UniqueSize(std::forward<Args>(args)...);
std::array<int, sz> arr{};
std::vector v{std::forward<Args>(args)...};
std::sort(v.begin(), v.end());
auto newEnd = std::unique(v.begin(), v.end());
std::copy(v.begin(), v.end(), arr.begin());
return arr;
}
int main()
{
// This compiles fine
constexpr auto sz = UniqueSize(2, 3, 4, 4, 5, 5);
static_assert(sz == 4);
std::array<int, sz> array{};
// This gives an error complaining that `sz` inside UniqueArray is not a constant expression.
// auto arr = UniqueArray(2, 3, 4, 4, 5, 5);
}
As explained in this SO post, parameters of consteval
function are not considered consteval
.
To allow what you are aiming for, you need to move the parameters to be template parameters. Something like this should work:
template <auto... args>
consteval auto UniqueBigArray() {
using TYPE = std::common_type_t<decltype(args)...>;
std::array<TYPE, sizeof...(args)> arr{args...};
std::sort(arr.begin(), arr.end());
auto last = std::unique(arr.begin(), arr.end());
return std::pair {arr, std::distance(arr.begin(), last)};
}
template <auto... args>
consteval auto UniqueSize() {
return UniqueBigArray<args...>().second;
}
template <auto... args>
consteval auto UniqueArray() {
constexpr auto p = UniqueBigArray<args...>();
std::array<typename decltype(p.first)::value_type, p.second> arr{};
std::copy(p.first.begin(), p.first.begin() + p.second, arr.begin());
return arr;
}
int main() {
constexpr auto sz = UniqueSize<2, 3, 4, 4, 5, 5>();
static_assert(sz == 4);
constexpr auto arr = UniqueArray<2, 3, 4, 4, 5, 5>();
static_assert(arr == std::array{2, 3, 4, 5});
constexpr auto arr2 = UniqueArray<3, 4.0, 4, 5, 5.5>();
static_assert(arr2 == std::array{3.0, 4.0, 5.0, 5.5});
}