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 ?
Thank you in advane.
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 paramaters. 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});
}