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

发布于 2024-10-31 17:42:34 字数 632 浏览 3 评论 0原文

我刚刚注意到新标准定义了 min(a,b)max(a,b) without 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

Any Reason Why those can not be constexpr?

I just noticed that the new standard defines min(a,b) and max(a,b) without constexpr.

Examples from 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);

Isn't this a pity? I would have liked to write

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

instead of

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

Any reason why those can not be constexpr?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

枫林﹌晚霞¤ 2024-11-07 17:42:34

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

std::min and std::max are constexpr in C++14, which obviously means there isn't a good reason (these days) not to have them constexpr. Problem solved :-)

貪欢 2024-11-07 17:42:34

重要更新

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

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

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

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

但是 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 纯右值隐式转换为两个引用参数,将两个引用绑定到存储其值的临时对象。对于每个引用临时对象的情况,此转换的结果是一个glvalue(请参阅 4p3)。现在,函数调用替换采用这两个泛左值,并用这些泛左值替换函数体中所有出现的 ab

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

条件将要求对这些泛左值进行左值到右值的转换,这5.19p2 允许

  • 文字类型的泛左值,引用用常量表达式初始化的非易失性临时对象

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


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

Critical Update

The below analysis is wrong, because it confuses one important thing. The following statement I did missed one important detail, which requires an entirely different answer.

The unnamed reference max returns will refer to that operand.

The problem here is that function invocation substitution is done at that point. If the invocation susbstitution would include the lvalue to rvalue conversion on that glvalue that max yields, everything would be fine, because reading from a glvalue that refers to a temporary not of static storage duration is fine during computation of the constant expression. But since the read happens outside of function invocation substitution, the result of function invocation substitution is an lvalue. The respective text of the spec says

A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.

But the reference that max returns yields an lvalue that designates an object of unspecified storage duration. Function invocation substitution is required to yield a constant expression, not merely a core constant expression. So max(sizeof(A), sizeof(B)) is not guaranteed to work.

The following (older) text needs to be read taking the above into account.


I can't see any reason at the moment why you wouldn't want to stick a constexpr there. Anyway, the following code definitely is useful

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

Contrary to what other answers write, I think this is legal. Not all instantiations of max are required to be constexpr functions. The current n3242 says

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is not a constexpr function or constexpr constructor.

If you call the template, argument deduction will yield a function template specialization. Calling it will trigger function invocation substitution. Consider the following call

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

It will first do an implicit conversion of the two size_t prvalues to the two reference parameters, binding both references to temporary objects storing their value. The result of this conversion is a glvalue for each case that refers to a temporary object (see 4p3). Now function invocation substitution takes those two glvalues and substitutes all occurences of a and b in the function body by those glvalues

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

The condition will require lvalue to rvalue conversions on these glvalues, which are allowed by 5.19p2

  • a glvalue of literal type that refers to a non-volatile temporary object initialized with a constant expression

The conditional expression will yield a glvalue to either the first or second operand. The unnamed reference max returns will refer to that operand. And the final lvalue to rvalue conversion happening in the array dimension size specification will be valid by the same rule quoted above.


Note that initializer_list currently doesn't have constexpr member functions. This is a known limitation and will be handled post-C++0x, most likely making those members constexpr.

旧伤还要旧人安 2024-11-07 17:42:34

C++14 中包含 std::min()std::max()constexpr 版本表明,没有基本的制作这些函数(的版本)constexpr的障碍。当 constexpr 添加到 C++11 时,似乎没有足够早地考虑这一点。

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

The inclusion of constexpr versions of std::min() and std::max() in C++14 demonstrates that there's no fundamental obstacle to making (versions of) these functions constexpr. It seems that this wasn't considered early enough when constexpr was added to C++11.

Obviously, for the versions where a comparison function is provided, that function must itself be constexpr for the template expansion to succeed.

恋你朝朝暮暮 2024-11-07 17:42:34

如果您使用常量表达式作为参数调用它们,则 minmax 仅是常量表达式。由于它们的用途比这更普遍,因此您无法做出声明。

以下是 维基百科关于 constexpr 的说法(强调额外)。我知道维基百科不是最终的参考,但我相信在这种情况下它是正确的。

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

min and max are only constant expressions if you call them with constant expressions as arguments. Since they're meant to be much more generally usable than that, you can't make the declaration.

Here's what Wikipedia says about constexpr (emphasis added). I know Wikipedia is not the ultimate reference, but I believe it's correct in this case.

The use of constexpr on a function
imposes very strict limitations on
what that function can do. First, the
function must have a non-void return
type. Second, the function contents
must be of the form: return expr.
Third, expr must be a constant
expression, after argument
substitution. This constant expression
may only call other functions defined
as constexpr, or it may use other
constant expression data variables.

并安 2024-11-07 17:42:34

我的猜测是,在一般情况下,operator<(T, T) 也不能保证是 constexpr。

My guess is that, in the general case, operator<(T, T) is not guaranteed to be constexpr either.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文