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.
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
A reference constant expression is an lvalue core constant expression that designates an object with static storage duration or a function.
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.
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
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.
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
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
- a glvalue of literal type that refers to a non-volatile temporary object initialized with a constant expression
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.
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
.