重要更新
下面的分析是错误的,因为它混淆了一件重要的事情。以下陈述我确实遗漏了一个重要细节,这需要一个完全不同的答案。
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并将函数体中a和b的所有出现替换为这些glvalues
return (<glval.a>) > (<glval.b>) ? (<glval.a>) : (<glval.b>);
条件将要求在这些 glvalues 上进行左值到右值的转换,这是 5.19p2 所允许的
- 字面量类型的左值,指的是用常量表达式初始化的非易失性临时对象
条件表达式将为第一个或第二个操作数产生一个泛左值。未命名的引用max 返回将引用该操作数。并且在数组维度大小规范中发生的最终左值到右值转换将根据上面引用的相同规则有效。
请注意,initializer_list 目前没有 constexpr 成员函数。这是一个已知限制,将在 C++0x 之后处理,很可能使这些成员成为constexpr。