Подтвердить что ты не робот

Почему сужение не влияет на разрешение перегрузки?

Рассмотрим следующее:

struct A {
    A(float ) { }
    A(int ) { }
};

int main() {
    A{1.1}; // error: ambiguous
}

Это не скомпилируется с ошибкой о неоднозначной перегрузке A::A. Оба кандидата считаются жизнеспособными, поскольку требование просто:

Во-вторых, для F, чтобы быть жизнеспособной функцией, для каждого аргумента будет существовать неявная последовательность преобразований (13.3.3.1), которая преобразует этот аргумент в соответствующий параметр F.

Пока существует неявная последовательность преобразований от double до int, перегрузка A(int ) фактически не жизнеспособна (в каноническом, не С++ - стандартном смысле), что предполагает сужение преобразования и, следовательно, быть плохо сформированным.

Почему сужение конверсий не рассматривается в процессе определения жизнеспособных кандидатов? Существуют ли другие ситуации, когда перегрузка считается неоднозначной, хотя только один кандидат является жизнеспособным?

4b9b3361

Ответ 1

Проблема заключается в том, что сужение конверсий может быть обнаружено не на основе типов.

Есть очень сложные способы генерации значений во время компиляции в С++.

Блокировка сужения конверсий - это хорошо. Сделать разрешение перегрузки С++ еще сложнее, чем это уже есть, это плохо.

Игнорирование сужающих правил преобразования при определении разрешения перегрузки (что делает разрешение перегрузки чисто о типах), а затем обнуление, когда выбранная перегрузка приводит к сужению конверсии, сохраняет разрешение перегрузки еще более сложным и добавляет обнаруживать и предотвращать сужение конверсий.

Два примера, в которых может быть только один кандидат, - это функции шаблона, которые не могут "задерживаться", во время создания экземпляра и инициализации списка копий (где рассматриваются конструкторы explicit, но если они выбраны, вы получаете сообщение об ошибке). Точно так же, с учетом того, что разрешение перегрузки при перегрузке сделает решение с перегрузкой еще более сложным, чем оно уже есть.

Теперь можно спросить, почему бы не сворачивать сужение преобразования только в систему типов?

Преобразование сужения будет чисто основанным на типе, было бы нежизнеспособным. Такие изменения могут нарушить огромное количество "устаревшего" кода, который мог бы доказать, что компилятор является действительным. Усилия, необходимые для подметания базы кода, гораздо более полезны, когда большинство ошибок являются фактическими ошибками, а не новой версией компилятора, являющейся рывком.

unsigned char buff[]={0xff, 0x00, 0x1f};

это завершится неудачей при преобразовании сужения на основе типа, поскольку 0xff имеет тип int, и такой код очень распространен.

Если бы такой код требовал бессмысленной модификации литералов int до unsigned char литералов, то вероятность того, что развертка закончилась бы тем, что мы установили флаг, чтобы сообщить компилятору заткнуться о глупой ошибке.

Ответ 2

  • Сужение - это то, о чем компилятор знает только о встроенных типах. Определенное пользователем неявное преобразование не может быть помечено как сужение или нет.

  • Сужение конверсий не должно допускаться, в первую очередь, подразумевать. (К сожалению, это требовалось для совместимости C. Это было несколько исправлено с инициализацией {}, запрещающей сужение для встроенных типов.)

Учитывая это, имеет смысл, что правила перегрузки не утруждают себя упоминанием этого особого случая. Это может быть случайным удобством, но это не все, что ценно. ИМО лучше вообще иметь меньше факторов, связанных с разрешением перегрузки, и отклонять большее количество вещей как неоднозначное, заставляя программиста явно разрешать такие вещи.


Кроме того, double to float является сужающим преобразованием, когда double не является константным выражением или если двойной слишком большой.

#include <iostream>
#include <iomanip>

int main() {
    double d{1.1};
    float f{d};
    std::cout << std::setprecision(100) << d << " " << f << '\n';
}

Это приведет к ошибке:

main.cpp:7:13: error: non-constant-expression cannot be narrowed from type 'double' to 'float' in initializer list [-Wc++11-narrowing]
    float f{d};
            ^