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

时间:2021-08-24 22:57:14

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

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

Examples from 25.4.7, [alg.min.max]:

从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?

有什么理由可以解释为什么这些不能是constexpr?

5 个解决方案

#1


13  

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.

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

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

这里的问题是,函数调用替换在此时完成。如果调用susb宪法将在glvalue产生的最大值上包含lvalue到rvalue的转换,那么一切都没问题,因为在计算常量表达式时,从glvalue中读取一个暂时的而不是静态存储持续时间的glvalue是没问题的。但是由于读取发生在函数调用替换之外,函数调用替换的结果是一个lvalue。规范的相应文本说

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

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

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.

但是,max返回的引用会产生一个lvalue,它指定一个未指定存储时间的对象。函数调用替换需要产生一个常量表达式,而不仅仅是一个核心常量表达式。所以max(sizeof(A), sizeof(B)并不能保证有效。

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

与其他答案相反,我认为这是合法的。并不是所有的max实例都被要求是constexpr函数。当前n3242说

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.

如果constexpr函数模板或类模板的成员函数的实例化模板专门化不能满足constexpr函数或constexpr构造函数的需求,那么专门化就不是constexpr函数或constexpr构造函数。

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

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

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

条件将需要lvalue到这些glvalues上的rvalue转换,该值允许为5.19p2。

  • a glvalue of literal type that refers to a non-volatile temporary object initialized with a constant expression
  • 文本类型的glvalue,指用常量表达式初始化的非易失性临时对象

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.

条件表达式将为第一个或第二个操作数生成一个glvalue。未命名引用max返回将引用该操作数。在数组维度大小规范中发生的rvalue转换的最后一个lvalue将根据上面引用的相同规则有效。


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.

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

#2


12  

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 :-)

min和std: max是c++ 14中的constexpr,这显然意味着(现在)没有一个好的理由不使用它们。问题解决了:-)

#3


1  

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.

在c++ 14中包含了std::min()和std: max()的constexpr版本,这说明在构造(版本)这些函数时没有根本的障碍。似乎在将constexpr添加到c++ 11时还没有考虑到这一点。

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

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

#4


-1  

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.

下面是*对constexpr(增加了强调)的描述。我知道*不是最终的参考,但我相信它在这个案例中是正确的。

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.

对函数使用constexpr会对函数的功能施加非常严格的限制。首先,函数必须具有非空返回类型。第二,函数内容必须为:return expr。第三,在参数替换之后,expr必须是一个常量表达式。这个常量表达式可能只调用定义为constexpr的其他函数,也可能使用其他常量表达式数据变量。

#5


-3  

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

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

#1


13  

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.

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

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

这里的问题是,函数调用替换在此时完成。如果调用susb宪法将在glvalue产生的最大值上包含lvalue到rvalue的转换,那么一切都没问题,因为在计算常量表达式时,从glvalue中读取一个暂时的而不是静态存储持续时间的glvalue是没问题的。但是由于读取发生在函数调用替换之外,函数调用替换的结果是一个lvalue。规范的相应文本说

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

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

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.

但是,max返回的引用会产生一个lvalue,它指定一个未指定存储时间的对象。函数调用替换需要产生一个常量表达式,而不仅仅是一个核心常量表达式。所以max(sizeof(A), sizeof(B)并不能保证有效。

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

与其他答案相反,我认为这是合法的。并不是所有的max实例都被要求是constexpr函数。当前n3242说

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.

如果constexpr函数模板或类模板的成员函数的实例化模板专门化不能满足constexpr函数或constexpr构造函数的需求,那么专门化就不是constexpr函数或constexpr构造函数。

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

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

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

条件将需要lvalue到这些glvalues上的rvalue转换,该值允许为5.19p2。

  • a glvalue of literal type that refers to a non-volatile temporary object initialized with a constant expression
  • 文本类型的glvalue,指用常量表达式初始化的非易失性临时对象

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.

条件表达式将为第一个或第二个操作数生成一个glvalue。未命名引用max返回将引用该操作数。在数组维度大小规范中发生的rvalue转换的最后一个lvalue将根据上面引用的相同规则有效。


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.

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

#2


12  

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 :-)

min和std: max是c++ 14中的constexpr,这显然意味着(现在)没有一个好的理由不使用它们。问题解决了:-)

#3


1  

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.

在c++ 14中包含了std::min()和std: max()的constexpr版本,这说明在构造(版本)这些函数时没有根本的障碍。似乎在将constexpr添加到c++ 11时还没有考虑到这一点。

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

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

#4


-1  

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.

下面是*对constexpr(增加了强调)的描述。我知道*不是最终的参考,但我相信它在这个案例中是正确的。

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.

对函数使用constexpr会对函数的功能施加非常严格的限制。首先,函数必须具有非空返回类型。第二,函数内容必须为:return expr。第三,在参数替换之后,expr必须是一个常量表达式。这个常量表达式可能只调用定义为constexpr的其他函数,也可能使用其他常量表达式数据变量。

#5


-3  

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

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