Лучшая жизнеспособная функция преобразования - PullRequest
2 голосов
/ 11 июня 2019

В следующей программе, какую (если есть) функцию преобразования следует выбрать и почему?

int r;
struct B {};
struct D : B {};
struct S {
  D d;
  operator D&(){r=1; return d;} // #1
  operator B&(){r=2; return d;} // #2
};
int main() {
  S s;
  B& b = s;
  return r;
}

Как gcc, так и clang select выбирают функцию преобразования # 2. Но почему?

Стандарт говорит :

(1) При условиях, указанных в [dcl.init.ref], ссылка может быть напрямую связана с результатом применения функции преобразования к выражению инициализатора. Разрешение перегрузки используется для выбора функции преобразования, которая будет вызвана. Предполагая, что «ссылка на cv1 T» - это тип инициализируемой ссылки, а «cv S» - это тип выражения инициализатора, а S - тип класса, функции-кандидаты выбираются следующим образом:

(1.1) - Рассматриваются функции преобразования S и его базовых классов. Те неявные функции преобразования, которые не скрыты в S и дают тип «lvalue ссылка на cv2 T2» (при инициализации lvalue ссылка или rvalue ссылка на функцию) или «cv2 T2» или «rvalue ссылка на cv2 T2» (когда инициализация ссылки на rvalue или ссылки на lvalue на функцию), где «cv1 T» совместима с «cv2 T2», являются кандидатами на функции. Для прямой инициализации те явные функции преобразования, которые не скрыты в S и дают тип «lvalue ссылка на cv2 T2» (при инициализации lvalue ссылка или rvalue ссылка на функцию) или «rvalue ссылка на cv2 T2» (при инициализации Ссылка rvalue или ссылка lvalue на функцию), где T2 является тем же типом, что и T, или может быть преобразован в тип T с преобразованием квалификации, также являются функциями-кандидатами.

(2) Список аргументов имеет один аргумент, который является выражением инициализатора. [Примечание: этот аргумент будет сравниваться с неявным параметром объекта функций преобразования. - конец примечания ]

Здесь у нас есть две функции-кандидата # 1 и # 2. Оба являются жизнеспособными - если один из них удален, программа все еще компилируется. Обе функции преобразования принимают только неявный аргумент и имеют одинаковые cv- и ref-квалификации. Так что ни один из них не должен быть лучшим, а программа не должна компилироваться. Почему он компилируется?

1 Ответ

5 голосов
/ 11 июня 2019

Ну, как вы знаете, разрешение перегрузки происходит в три этапа: (1) перечисление функций-кандидатов;(2) определить, какие функции-кандидаты являются жизнеспособными;(3) выберите наилучшую жизнеспособную функцию.

Согласно [over.match.best] / 1:

... жизнеспособная функция F1 определена каклучшая функция, чем у другой жизнеспособной функции F2, если для всех аргументов i , ICS i (F1) не хуже последовательности преобразования, чем ICS i (F1), а затем

  • для некоторого аргумента j , ICS j (F1) - лучшая последовательность преобразования, чем ICS j (F2) или, если не так,
  • , контекст является инициализацией с помощью пользовательского преобразования (см. 11.6, 16.3.1.5 и 16.3.1.6) и стандартной последовательности преобразованияпереход от типа возврата F1 к типу назначения (т. е. к типу инициализируемой сущности) является лучшей последовательностью преобразования, чем стандартная последовательность преобразования из типа возврата F2 к типу назначения [пример ...] или, если не так,
  • [... дальнейшие правила тай-брейка ...]

Неявное преобразование rот s до неявного параметра объекта, равного # 1 или # 2, является преобразование идентификатора, поэтому ICS1 (# 1) и ICS2 (# 1) неразличимы, и здесь важен второй пункт маркированного списка.В случае # 1 требуется преобразование из производного в базовое значение для преобразования из возвращаемого типа функции преобразования, а именно D&, в требуемый тип, а именно B&.В случае № 2 стандартной последовательностью преобразования является преобразование идентификаторов (B& в B&), что лучше.Поэтому в этом контексте функция # 2 выбрана лучше, чем # 1.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...