std::max 统一初始化和 constexpr int 表现出意外

std::max behaves unexpected with uniform initialization and constexpr int

我正在试验 std::max。我试图通过统一初始化(花括号)传递整数 constexpr,以将它们与浮点变量进行比较。

实验 a):调用 std::max() 与 double/int 混合

    double a = 3.0;
    int b = 5;
    auto res = std::max(a, b);

不编译。 Clang 报告 error: no matching function for call to 'max'。这当然可以。

实验b):使用大括号+constexpr int进行非逆向转换

    double a = 3.0;
    constexpr int b = 5;
    auto res = std::max(a, {b});

按预期编译和工作:returns 值为 5.0 的双精度值。

实验 c):与 b) 相同,但交换 std::max.

的参数
    double a = 3.0;
    constexpr int b = 5;
    auto res = std::max({b}, a);

在 gcc 和 clang 下都不编译。为什么?

Clang 报告 error: called object type 'double' is not a function or function pointer

存在 std::max 的重载,如下所示(来自 cppreference.com):

template< class T, class Compare >
constexpr T max( std::initializer_list<T> ilist, Compare comp );

这比

更适合您的通话 auto res = std::max({b}, a);
template< class T >
constexpr const T& max( const T& a, const T& b );

您正在尝试调用,因为 {b} 可以推断为 std::initializer_list<int> 并且该调用在两个参数中都具有完全匹配的转换等级,而您要调用的重载需要从 intdouble 的转换不是完全匹配。

第二个参数被认为是比较操作调用的Compare仿函数,但是调用double显然失败了。如果第二个参数不可调用,则重载不会被禁用,这就是它仍然被选中的原因。

auto res = std::max(a, {b}); 不会发生这种情况,因为第二个参数没有 std::initializer_list 参数的重载,因此只有您要调用的重载是可行的。初始化列表使第二个参数成为非推导上下文,这就是为什么它与 auto res = std::max(a, b); 不同的原因,后者由于两个参数之间的模板参数推导不匹配而失败。

在你的第三个 'experiment' 中,你有一个初始化列表作为 std::max 的第一个参数;因此,编译器试图使用以下模板 (see cppreference):

template< class T, class Compare >
T max( std::initializer_list<T> ilist, Compare comp );

其中第二个参数需要是一个比较函数。