Search code examples
c++language-lawyerstdcompile-time

Is std::less supposed to allow comparison of unrelated pointers at compile-time?


Consider this code:

#include <functional>
#include <typeinfo>

template <typename T>
inline constexpr const void *foo = &typeid(T);

int main()
{
    constexpr bool a = std::less<const void*>{}(foo<int>, foo<float>);
} 

Run on gcc.gotbolt.org

If I use < instead of std::less here, the code doesn't compile. This is not surprising, because the result of a relational pointer comparsion is unspecified if the pointers point to unrelated objects, and apparently such a comparsion can't be done at compile-time.

<source>:9:20: error: constexpr variable 'a' must be initialized by a constant expression
    constexpr bool a = foo<int> < foo<float>;
                   ^   ~~~~~~~~~~~~~~~~~~~~~
<source>:9:33: note: comparison has unspecified value
    constexpr bool a = foo<int> < foo<float>;
                                ^

The code still doesn't compile, even if I use std::less. The compiler error is the same. std::less appears to be implemented as < in at least libstdc++ and libc++; I get the same results on GCC, Clang, and MSVC.

However, the cppreference page about std::less claims that:

  1. Its operator() is constexpr.

  2. It magically implements strict total order on pointers, i.e. can be used to compare unrelated pointers with sensible results.

So, is it a bug in all those compilers, or am I missing some detail about std::less that makes the code above ill-formed?


Solution

  • I don't think there's a clear answer to the question that you're asking. This is a specific case of LWG 2833: marking a library function constexpr does not explain the circumstances under which calling the function will yield a constant expression.

    Until this issue is resolved, I think you simply cannot rely on std::less being able to compare unrelated pointers at compile time.