关键更新
下面的分析是错误的,因为它混淆了一件重要的事情。以下陈述我确实错过了一个重要的细节,这需要一个完全不同的答案。
未命名的引用max
返回将引用该操作数。
这里的问题是函数调用替换是在那个时候完成的。如果调用替换将包括产生的左值到右值的转换,那么max
一切都会很好,因为在计算常量表达式期间从引用临时非静态存储持续时间的左值读取是很好的。但是由于读取发生在函数调用替换之外,函数调用替换的结果是一个左值。规范的相应文本说
引用常量表达式是一个左值核心常量表达式,它指定具有静态存储持续时间的对象或函数。
但是max
返回的引用会产生一个左值,该左值指定一个未指定存储持续时间的对象。需要函数调用替换来产生一个常量表达式,而不仅仅是一个核心常量表达式。所以max(sizeof(A), sizeof(B))
不能保证工作。
考虑到上述内容,需要阅读以下(较旧的)文本。
目前我看不出有什么理由让你不想把 a 放在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
两个引用参数,将两个引用绑定到存储其值的临时对象。此转换的结果是每个引用临时对象的情况的泛左值(参见 4p3)。现在函数调用替换采用这两个glvalues并用这些glvalues替换a
函数b
体中的所有出现
return (<glval.a>) > (<glval.b>) ? (<glval.a>) : (<glval.b>);
该条件将要求在这些 glvalue 上进行左值到右值转换,这是 5.19p2 所允许的
- 一个字面量类型的左值,它引用一个用常量表达式初始化的非易失性临时对象
条件表达式将为第一个或第二个操作数产生一个左值。未命名的引用max
返回将引用该操作数。并且在数组维度大小规范中发生的最终左值到右值转换将根据上面引用的相同规则有效。
请注意,initializer_list
当前没有constexpr
成员函数。这是一个已知的限制,将在 C++0x 之后处理,很可能使这些成员成为constexpr
.