提问者:小点点

std::不应该允许在编译时比较不相关的指针吗?


请考虑以下代码:

#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>);
} 

在gcc.gotbolt.org上运行

如果我在这里使用而不是std::less,则代码无法编译。 这并不奇怪,因为如果指针指向不相关的对象,关系指针比较的结果是未指定的,而且显然在编译时无法进行这样的比较。

<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>;
                                ^

代码仍然无法编译,即使我使用std::less。 编译器错误相同。 std::less似乎至少在libstdc++和libc++中实现为<; 我在GCC,Clang和msvc上得到了相同的结果。

但是,关于std::less的cppreference页面声称:

>

  • 运算符()ConstExpr

    它神奇地在指针上实现了严格的总顺序,即可以用来将不相关的指针与可感的结果进行比较。

    那么,是所有这些编译器中的一个bug,还是我遗漏了一些有关std::less的细节,使得上面的代码格式不正确?


  • 共2个答案

    匿名用户

    我不认为你所问的问题有一个明确的答案。 这是LWG2833的一个具体情况:标记库函数constexpr并不能解释调用该函数是否会实际产生常量表达式。

    在这个问题得到解决之前,我认为您根本不能指望std::less能够在编译时比较不相关的指针。

    匿名用户

    要成为有效的ConstExpr函数,它应该具有结果为ConstExpr的参数,而不是所有参数。

    例如

    constexpr int foo(bool b) { if (!b) throw 42; return 42; }
    

    是有效的,则可以在constexpr中使用f(true)(即使f(false)不能)。

    constexpr int a[2]{};
    constexpr bool b = std::less<const void*>{}(&a[0], &a[1]);
    

    有效并且足以允许less::operator()constexpr

    我认为在标准中没有指定哪些范围/值对constexpr是正确的。

    所以所有编译器都是正确的。