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