std::max() and std::min() not constexpr
std::max() and std::min() not constexpr
我刚刚注意到新标准定义了min(a,b)
和max(a,b)
,没有 constexpr
。
取自25.4.7,[g.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
?
std::min和std::max在c++ 14中是constexpr,这显然意味着(现在)没有理由不使用它们。问题解决:-)
紧急更新
下面的分析是错误的,因为它混淆了一件重要的事情。下面我所做的陈述漏掉了一个重要的细节,这需要一个完全不同的答案。
这里的问题是函数调用替换在此时已完成。如果调用替换将包括
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
前置值隐式转换为两个引用形参,将两个引用绑定到存储其值的临时对象。这种转换的结果是对每个引用临时对象的情况都有glvalue(参见4p3)。函数调用替换用这两个glvalue替换函数体中出现的所有a
和b
return (<glval.a>) > (<glval.b>) ? (<glval.a>) : (<glval.b>);
条件将要求在这些左值上进行左值到右值的转换,这是5.19p2
所允许的
- 一个文字类型的全局值,它引用一个用常量表达式
初始化的非易失性临时对象
条件表达式将生成第一个或第二个操作数的全局值。未命名的引用max
返回指向该操作数。数组尺寸大小规范中发生的最后的左值到右值转换将根据上面引用的相同规则有效。
注意initializer_list
目前没有constexpr
成员函数。这是一个已知的限制,将在c++ 0x之后处理,最有可能使这些成员成为constexpr
.
在c++ 14中包含std::min()
和std::max()
的constexpr
版本表明没有根本的障碍使这些函数(版本)成为constexpr
。当constexpr
被添加到c++ 11中时,似乎没有足够早地考虑到这一点。
显然,对于提供了比较函数的版本,该函数本身必须是constexpr
才能使模板展开成功。
min
和max
只有在使用常量表达式作为参数调用时才是常量表达式。因为它们的目的是更普遍地可用,所以你不能做声明。
这是维基百科对constexpr
的解释。我知道维基百科不是最终的参考,但我相信它在这种情况下是正确的。
constexpr
在函数上的使用对…施加了非常严格的限制这个函数能做什么。首先,函数必须有非空返回值类型。二是功能内容格式必须为:return expr。第三,expr必须是常量表达式,在论证之后替换。这个常数表达式可以只调用其他定义的函数吗作为constexpr,也可以使用other
我的猜测是,在一般情况下,操作符<(T, T)也不能保证是constexpr
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- 为什么std::isnan 不是 constexpr?
- constexpr上下文中std::initializer_list的验证
- constexpr begin of a std::array
- 为什么我不能在 constexpr lambda 函数中使用 std::tuple
- 使用 std::分配器在 constexpr 中进行默认初始化
- 为什么 std::launder 是一个 constexpr 函数?
- std::constexpr 上下文中的可选赋值运算符
- 是否已经有一个 constexpr std::bit_cast 与 g++ 一起使用
- 为什么 std::get<T> 其中 T 是调用 constexpr 函数失败的结果?
- 我可以制作一个std::set的constexpr对象吗
- 使用 constexpr 初始化 std.array 中的对象
- 使用 constexpr 作为 std::array size
- 为什么 std::array 的运算符 ==() 没有标记为 constexpr?
- C++ standard: ODR and constexpr std::string_view
- 类、constexpr 构造函数和 std::string
- 在编译时通过 constexpr 或模板函数获取多维 std::array 的大小
- 使用 constexpr 和 std::array 进行静态初始化来替换动态初始化的 std::vector 的闰年
- Constexpr 可变参数模板和解包 std::array
- 是否可以以编程方式初始化 constexpr std::array 成员