Следующее определение функции min
template <typename T, typename U>
constexpr auto
min(T&& t, U&& u) -> decltype(t < u ? t : u)
{
return t < u ? t : u;
}
есть проблема: кажется, что писать
min(10, 20) = 0;
Это было протестировано с Clang 3.5 и g++ 4.9.
Решение простое, просто используйте std::forward
, чтобы восстановить "rvalue-ness" аргументов, т.е. измените тело и decltype
, чтобы сказать
t < u ? std::forward<T>(t) : std::forward<U>(u)
Однако я затрудняюсь объяснить, почему первое определение не вызывает ошибки.
Учитывая мое понимание переадресации и универсальных ссылок, и t
, и u
выводят свои типы аргументов как int&&
при передаче целочисленных литералов. Однако в теле min
аргументы имеют имена, поэтому они являются lvalue. Теперь в игру вступают действительно сложные правила условного оператора, но Я думаю, что подходящая строка:
- Оба E2 [и] E3 являются значениями gl одного и того же типа. В этом случае результат имеет тот же тип и категорию значения.
и поэтому возвращаемый тип operator?:
тоже должен быть int&&
, не так ли? Однако (насколько я могу судить) и Clang, и g++ имеют min(int&&, int&&)
, возвращающую ссылку lvalue int&
, что позволяет мне присваивать результат.
Ясно, что в моем понимании есть пробел, но я не уверен, что именно мне не хватает. Может ли кто-нибудь объяснить мне, что именно здесь происходит?
ИЗМЕНИТЬ:
Как правильно указывает Найл, проблема здесь не в условном операторе (который, как и ожидалось, возвращает lvalue типа int&&
), а в операторе decltype
. Правила для decltype
говорят
если категорией значения выражения является lvalue, то decltype указывает T&
поэтому возвращаемое значение функции становится int&& &
, которое по правилам свертывания ссылок С++ 11 превращается в простое int&
(вопреки моему ожидаемому int&&
).
Но если мы используем std::forward
, мы превращаем второй и третий аргументы operator?:
(назад) в значения r, а именно, значения x. Так как значения x по-прежнему являются значениями gl (вы не отстаёте?), применяется то же правило условного оператора, и мы получаем результат того же типа и категории значений: то есть int&&
, которое является значением x.
Теперь, когда функция возвращается, она запускает другое правило decltype
:
если категория значения выражения xvalue, то decltype указывает T&&
На этот раз свертывание ссылки дает нам int&& && = int&&
и, что более важно, функция возвращает xvalue. Это делает незаконным присваивание возвращаемому значению, как нам бы хотелось.
std::forward
, а неstd::move
, поэтому функция по-прежнему работает с аргументами lvalue). Мне любопытно, почему первое (неправильное) определение делает то, что делает. - person Tristan Brindle   schedule 14.10.2014decltype()
, если удаляемый возвращаемый тип (т.е.auto min(...) { ... }
) и позволяющий компилятору вывести его, он возвращаетint
. - person Niall   schedule 14.10.2014