std::max() 和 std::min() 不是 constexpr

Posted

技术标签:

【中文标题】std::max() 和 std::min() 不是 constexpr【英文标题】:std::max() and std::min() not constexpr 【发布时间】:2011-08-02 02:03:52 【问题描述】:

我刚刚注意到新标准定义了min(a,b)max(a,b) 没有 constexpr

25.4.7 中的示例,[alg.min.max]:

template<class T> const T& min(const T& a, const T& b);
template<class T> T min(initializer_list<T> t);

这不是很遗憾吗?我本来想写的

char data[ max(sizeof(A),sizeof(B)) ];

而不是

char data[ sizeof(A) > sizeof(B) ? sizeof(A) : sizeof(B) ];
char data[ MAX(sizeof(A),sizeof(B)) ]; // using a macro

那些不能是constexpr的任何原因?

【问题讨论】:

【参考方案1】:

我的猜测是,在一般情况下,也不能保证 operator

【讨论】:

因此,它可以被模板化,constexpr 仅适用于本机操作数类型。【参考方案2】:

minmax 仅当您使用常量表达式作为参数调用它们时才是常量表达式。由于它们的用途远不止于此,因此您不能进行声明。

这就是Wikipedia says about constexpr(强调添加)。我知道 Wikipedia 不是最终参考,但我相信在这种情况下它是正确的。

在函数上使用constexpr 施加了非常严格的限制 该功能可以做什么。首先, 函数必须有一个非空返回 类型。二、功能内容 必须是以下形式:return expr。 三、expr必须是常数 表达式,在参数之后 替代。这个常量表达式 只能调用定义的其他函数 作为 constexpr,或者它可以使用其他 常量表达式数据变量。

【讨论】:

参数替换后constexpr 不会取消变量参数的资格。 我有点不同意。非常简单的实现T max(T a,T b) return a&gt;b ? a : b; 有资格被声明为constexpr。这个声明独立于它的use。如果我使用它与constexpr 参数,我可以得到constexpr 结果。 马克的回答不正确。至少在 C++11 中,minmax 绝不是常量表达式,因为它们在标准库中没有声明为 constexpr。但是 OP 问道:为什么 不是 他们声明了constexpr @MarkRansom,是的,我不同意。 constexpr 本质上意味着“我可以保持不变”,而不是“我必须始终保持不变”。 (对于后一种情况,我们已经有 const 变量。)在 C++11 中试试这个:constexpr int min(int a, int b) return a&lt;b ? a : b; int main(int argc, char **argv) char a[min(42,43)]; static_assert(sizeof(a)==42, "test"); printf("%d\n", min(argc,5)); @MarkRansom cprogramming.com/c++11/…【参考方案3】:

重要更新

下面的分析是错误的,因为它混淆了一件重要的事情。以下陈述我确实遗漏了一个重要细节,这需要一个完全不同的答案。

max 返回的未命名引用将引用该操作数。

这里的问题是,函数调用替换此时完成。如果调用替换将包括在max 产生的那个glvalue 上的左值到右值转换,那么一切都会好的,因为在计算常量期间从引用临时非静态存储持续时间的glvalue 读取是好的表达式。但是由于读取发生在函数调用替换之外,函数调用替换的结果是一个左值。规范的相应文本说

引用常量表达式是一个左值核心常量表达式,它指定具有静态存储持续时间的对象或函数。

但是max 返回的引用会产生一个左值,该左值指定一个未指定存储期限的对象。需要函数调用替换来产生一个常量表达式,而不仅仅是一个核心常量表达式。所以max(sizeof(A), sizeof(B)) 不保证能正常工作。

阅读以下(较旧的)文本时需要考虑到上述内容


目前我看不出有什么理由让您不想在此处粘贴constexpr。不管怎样,下面的代码肯定是有用的

template<typename T> constexpr
T const& max(T const& a, T const& b) 
  return a > b ? a : b;

与其他答案所写的相反,我认为这是合法的。并非max 的所有实例化都必须是 constexpr 函数。目前的n3242说

如果 constexpr 函数模板或类模板的成员函数的实例化模板特化无法满足 constexpr 函数或 constexpr 构造函数的要求,则该特化不是 constexpr 函数或 constexpr 构造函数。

如果你调用模板,参数推导将产生一个函数模板特化。调用它会触发函数调用替换。考虑以下调用

int a[max(sizeof(A), sizeof(B))];

它将首先将两个size_t prvalues 隐式转换为两个引用参数,将两个引用绑定到存储其值的临时对象。对于每个引用临时对象的情况,这种转换的结果是一个 glvalue(参见 4p3)。现在函数调用替换采用这两个glvalues并将函数体中ab的所有出现替换为这些glvalues

return (<glval.a>) > (<glval.b>) ? (<glval.a>) : (<glval.b>);

条件将要求在这些 glvalues 上进行左值到右值的转换,这是 5.19p2 所允许的

字面量类型的左值,指的是用常量表达式初始化的非易失性临时对象

条件表达式将为第一个或第二个操作数产生一个泛左值。未命名的引用max 返回将引用该操作数。并且在数组维度大小规范中发生的最终左值到右值转换将根据上面引用的相同规则有效。


请注意,initializer_list 目前没有 constexpr 成员函数。这是一个已知限制,将在 C++0x 之后处理,很可能使这些成员成为constexpr

【讨论】:

我同意你的推理,虽然我不能完全理解你的“右值”、“左值”魔法。我在标准中看到了这些定义,但我想必须更深入地挖掘。 C++ 委员会建议函数调用替换可以产生一个引用临时值的左值。 g++ 的行为方式是这样,但 clang 目前按照所写的方式实现标准。【参考方案4】:

std::min 和 std::max are constexpr 在 C++14 中,这显然意味着(这些天)没有充分的理由不让它们 constexpr。问题解决了:-)

【讨论】:

我认为这应该是评论【参考方案5】:

在 C++14 中包含 constexpr 版本的 std::min()std::max() 表明,制作这些函数 (版本) constexpr 没有根本障碍。在 C++11 中添加constexpr 时似乎还不够早。

显然,对于提供比较函数的版本,该函数本身必须是constexpr,模板扩展才能成功。

【讨论】:

以上是关于std::max() 和 std::min() 不是 constexpr的主要内容,如果未能解决你的问题,请参考以下文章

在 vector<double> 上使用 std::max_element

在 C++ 中获取中间值

关于NOMINMAX这个预处理宏

是否在空的初始化程序列表(并明确指定类型)上调用 std::min 未定义的行为?

VS2015--win32工程配置的一些想法之GdiplusTypes.h(470) : error C3861: 'min': identifier not found

std::minmax使用的注意事项