Ответ 1
Критическое обновление
Следующий анализ ошибочен, потому что он смущает одну важную вещь. В следующем утверждении я пропустил одну важную деталь, которая требует совершенно другого ответа.
Возврат без названия
max
будет ссылаться на этот операнд.
Проблема здесь в том, что подстановка функции вызывает done. Если подозрительная функция invocation будет включать в себя преобразование lvalue для rvalue для этого значения gl, которое max
дает, все будет хорошо, потому что чтение из glvalue, которое ссылается на временную не статическую длительность хранения, является прекрасным при вычислении константного выражения. Но так как чтение происходит вне подстановки функции invocation, результатом замены функции является lvalue. Соответствующий текст спецификации говорит
Постоянная ссылка выражение именующее ядра постоянное выражение, которое обозначает объект со статической продолжительностью хранения или функции.
Но ссылка, возвращаемая max
, дает значение l, которое обозначает объект неопределенной продолжительности хранения. Требуется подстановка функции вызова, чтобы получить постоянное выражение, а не просто выражение постоянной константы. Поэтому 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>);
Условие потребует lvalue для rvalue преобразований на этих glvalues, которые разрешены 5.19p2
- glvalue типа literal, который ссылается на энергонезависимый временный объект, инициализированный константным выражением
Условное выражение даст значение gl для первого или второго операнда. Возвращенная неназначенная ссылка max
будет ссылаться на этот операнд. И последнее преобразование lvalue в rvalue, происходящее в спецификации размера размера массива, будет действительным по тому же правилу, указанному выше.
Обратите внимание, что initializer_list
в настоящее время не имеет функций-членов constexpr
. Это известное ограничение и будет обрабатываться post-С++ 0x, скорее всего, с помощью этих членов constexpr
.