0

This is a simplified, reproducible version of my code:

type_id.h

template<typename>
void type_id() {}

typedef void(*type_id_t)();

c_sort.h (based on this answer)

template<typename Array>
constexpr void c_sort_impl(Array& array_) noexcept {
    using size_type = typename Array::size_type;
    size_type gap = array_.size();
    bool swapped = false;
    while ((gap > size_type{ 1 }) or swapped) {
        if (gap > size_type{ 1 }) {
            gap = static_cast<size_type> (gap / 1.247330950103979);
        }
        swapped = false;
        for (size_type i = size_type{ 0 }; gap + i < static_cast<size_type> (array_.size()); ++i) {
            if (array_[i] > array_[i + gap]) {
                auto swap = array_[i];
                array_[i] = array_[i + gap];
                array_[i + gap] = swap;
                swapped = true;
            }
        }
    }
}

template<typename Array>
constexpr Array c_sort(Array array_) noexcept {
    auto sorted = array_;
    c_sort_impl(sorted);
    return sorted;
}

foo.h

#include "type_id.h"
#include "c_sort.h"
#include <array>

template<class... Cs>
struct Foo
{
    constexpr static auto key =
        c_sort( std::array<type_id_t, sizeof...(Cs)>{ type_id<Cs>... } );
};

If I try to instantiate Foo, I get a compiler error telling me expression did not evaluate to a constant. Why is this? When initializing key, I only call functions marked with constexpr. What part of the expression can't be evaluated at compile-time?

JensB
  • 839
  • 4
  • 19
  • 1
    The static nature of the class member in combination with template value initialization seems to be the issue here. This question might help https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template – blurryroots Jan 06 '22 at 18:16
  • @user17732522 Just any template arguments. Gives me the same error regardless. – JensB Jan 06 '22 at 18:18
  • Please copy-paste the *full* and *complete* error output into the question. Compilers usually include some more information which could help figuring it out. – Some programmer dude Jan 06 '22 at 18:19
  • @user17732522 Gotcha. I will add that. But just to be clear, can you currently instantiate `Foo` with the above code on your end? – JensB Jan 06 '22 at 18:20
  • @JensB Nevermind, I should have tried using multiple template arguments. The answer is now obvious. See posted answer. – user17732522 Jan 06 '22 at 18:21

1 Answers1

3

A recent version of GCC or Clang will tell you the evaluation that failed to yield a constant expression. See https://godbolt.org/z/adhafn8v7

The problem is the comparison:

array_[i] > array_[i + gap]

A comparison between unequal function pointers (other than to check whether or not they are equal) has an unspecified result, and therefore is not permitted inside a constant expression evaluation.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • I see. So there is in other words no way whatsoever to sort a collection of pointers at compile time? – JensB Jan 06 '22 at 18:32
  • @JensB Also, it is not a good idea to sort unrelated pointers with `<` at runtime either. The result being unspecified means that it doesn't need to be consistent. For `std::less` you have a consistent order (including with `<`), but don't know anything about it aside from that. – user17732522 Jan 06 '22 at 18:41
  • @user17732522 Hm, I still can't get it to work after replacing the `>` operation with `std::greater{}(...)` (which seems to be the equivalent to `std::less` for greater than). Does it work on your end if you replace it with `std::greater`? – JensB Jan 06 '22 at 19:07
  • 1
    @JensB Seems it isn't so clear, see https://stackoverflow.com/questions/62492020/is-stdless-supposed-to-allow-comparison-of-unrelated-pointers-at-compile-time. But without that there is no way to compare unrelated pointers at compile-time. – user17732522 Jan 06 '22 at 19:16
  • @user17732522 What do you mean when you say "unrelated pointers"? They point to objects in the same array, how could they be more related than that? A genuine question, I don't know a lot of pointer terminology – JensB Jan 06 '22 at 19:21
  • 2
    @JensB You're confusing *pointers to array elements* with the array elements themselves (which are function pointers). The result of comparing two pointers into the same array is specified. The result of comparing pointers to different functions is unspecified, other than that they're unequal. – Brian Bi Jan 06 '22 at 19:22
  • 1
    @JensB For some suggestions on how to sort types at compile time, see [here](https://stackoverflow.com/questions/48723974/how-to-order-types-at-compile-time). It seems that all the techniques use `PRETTY_FUNCTION`, because there's no truly portable solution. – Brian Bi Jan 06 '22 at 19:25