Почему эта функция возвращает ссылку на lvalue с заданными аргументами rvalue?
Следующее определение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
восстановить "значение" аргументов, то есть изменить тело иdecltype
сказать
t < u ? std::forward<T>(t) : std::forward<U>(u)
Тем не менее, я затрудняюсь объяснитьЗачем первое определение не генерирует ошибку.
Учитывая мое понимание переадресации и универсальных ссылок, обаt
а такжеu
вывести их типы аргументов какint&&
когда переданы целочисленные литералы. Тем не менее, в телеmin
Аргументы имеют имена, поэтому они являются значениями. Теперьв игру вступают действительно сложные правила для условного оператора, но ясчитать соответствующая строка:
и, следовательно, тип возвращаемого значенияoperator?:
должно бытьint&&
а разве не так? Однако (насколько я могу судить) и Clang, и g ++ имеютmin(int&&, int&&)
возвращая ссылку lvalueint&
, что позволяет мне назначить результат.
Ясно, что в моем понимании есть пробел, но я точно не знаю, что мне не хватает. Кто-нибудь может объяснить мне точно, что здесь происходит?
РЕДАКТИРОВАТЬ:
Как правильно указывает Найл, проблема здесь не в условном операторе (который возвращает lvalue типаint&&
как и ожидалось), но сdecltype
, Правила дляdecltype
сказать
если категория значения выражения lvalue, тогда decltype указывает T &
поэтому возвращаемое значение функции становитсяint&& &
, который по правилам свертывания ссылок C ++ 11 превращается в простойint&
(вопреки моему ожиданиюint&&
).
Но если мы используемstd::forward
переворачиваем второй и третий аргументыoperator?:
(обратно) в rvalues - в частности, xvalues. Поскольку xvalues по-прежнему являются glvalues (вы не отставали от спины?), Применяется то же правило условного оператора, и мы получаем результат с тем же типом и категорией значений:int&&
который является xvalue.
Теперь, когда функция возвращается, она вызывает другойdecltype
правило:
если значение категории выражения равно xvalue, тогда decltype указывает T &&
На этот раз ссылка рушится дает намint&& && = int&&
и, что более важно, функция возвращает значение x. Это делает незаконным присваивать возвращаемое значение, как нам хотелось бы.