Рассмотрим следующий код .
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Здесь второй оператор преобразования выбирается разрешением перегрузки. Почему?
Насколько я понимаю, два оператора выводятся operator int &&() const
и operator int &() const
соответственно. Оба находятся в наборе жизнеспособных функций. Прочтение [over.match.best] не помогло мне понять, почему последнее лучше.
Почему последняя функция лучше первой?
template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;
заставляет это звонить первому.Ответы:
Возвращаемый оператор преобразования
T&
является предпочтительным, поскольку он более специализирован, чем возвращаемый оператор преобразованияT&&
.См. C ++ 17 [temp.deduct.partial] / (3.2):
и / 9:
источник
Выведенные операторы преобразования возвращаемого значения немного странные. Но основная идея заключается в том, что он действует как параметр функции, чтобы выбрать, какой из них используется.
И при выборе между
T&&
иT&
наT&
победы в правилах разрешения перегрузки. Это разрешить:работать.
T&&
может соответствовать lvalue, но когда доступны как lvalue, так и универсальные эталонные перегрузки, lvalue предпочтительнее.Правильный набор операторов преобразования, вероятно:
или даже
чтобы предотвратить неудачное продление жизни от укуса.
[СНиП]
Что в конечном итоге зависит от «более специализированных» правил при выборе перегрузок:
Таким образом,
operator T&&
по крайней мере, не так специализирован, какoperator T&
, в то время как ни одно из государств-правилoperator T&
не является, по крайней мереoperator T&&
, столь же специализированным, как иoperator T&
более специализированным, чемoperator T&&
.Более специализированные шаблоны выигрывают при разрешении перегрузки меньше, при прочих равных условиях.
источник
&&
vs&
при выборе перегрузок шаблона, но дразнить точный текст может занять некоторое время. ,Мы пытаемся инициализировать
int
изany
. Процесс для этого это:Выясните, как мы можем это сделать. То есть определяют всех наших кандидатов. Они происходят из неявных функций преобразования, в которые можно преобразовать
int
через стандартную последовательность преобразования ( [over.match.conv] ). Раздел содержит эту фразу:Выберите лучшего кандидата.
После шага 1 у нас есть два кандидата.
operator int&() const
иoperator int&&() const
оба из них считаются полезнымиint
для целей выбора функций-кандидатов. Какой лучший кандидат уступаетint
?У нас есть тай- брейкер, который предпочитает ссылки lvalue ссылкам rvalue ( [over.ics.rank] /3.2.3 ). Однако здесь мы не привязываем ссылку, и пример там несколько перевернут - это для случая, когда параметр является ссылкой lvalue vs rvalue.
Если это не применимо, тогда мы переходим к [over.match.best] /2.5 разрыва связи, предпочитая более специализированный шаблон функции.
Вообще говоря, эмпирическое правило заключается в том, что более конкретное преобразование является лучшим совпадением. Функция преобразования опорных значений lvalue более специфична, чем функция преобразования опорных ссылок, поэтому она предпочтительнее. В том, что
int
мы инициализируем, нет ничего, что требовало бы значения r (если бы мы вместо этого инициализировалиint&&
, тоoperator T&() const
кандидат не был бы кандидатом).источник
T&&
выигрывает, не так ли? Ах, но у нас нет привязанности к rvalue. Или мы? При выборе наших кандидатов некоторые слова должны были определить, как мы получили,int&
иint&&
это было обязательным?