Почему сужение не влияет на разрешение перегрузки?
Учтите следующее:
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 )
Перегрузка на самом деле не является жизнеспособной (в каноническом, не-C++- стандартном смысле) - это может привести к сужающему преобразованию и, следовательно, к неправильной форме.
Почему сужающиеся конверсии не рассматриваются в процессе определения жизнеспособных кандидатов? Существуют ли другие ситуации, когда перегрузка считается неоднозначной, несмотря на то, что жизнеспособен только один кандидат?
2 ответа
Проблема заключается в том, что сужающие преобразования могут быть обнаружены не на основе типов.
Есть очень сложные способы генерирования значений во время компиляции в C++.
Блокировка сужения конверсий - это хорошо. Сделать разрешение перегрузки в C++ еще сложнее, чем сейчас, - это плохо.
Игнорирование сужающих правил преобразования при определении разрешения перегрузки (которое делает разрешение перегрузки исключительно для типов), а затем происходит ошибка, когда выбранная перегрузка приводит к сужающему преобразованию, сохраняет разрешение перегрузки еще более сложным и добавляет способ обнаружения и предотвращения сужающие преобразования.
Два примера, где жизнеспособен только один кандидат, - это функции шаблонов, которые терпят неудачу "поздно", во время создания экземпляра и инициализации списка копирования (где explicit
конструкторы считаются, но если они выбраны, вы получаете ошибку). Точно так же наличие такого разрешения перегрузки, вызвавшего воздействие, сделало бы разрешение перегрузки еще более сложным, чем оно есть.
Теперь можно спросить, почему бы не свернуть сужающее преобразование чисто в систему типов?
Создание сужающего преобразования, основанного исключительно на типах, было бы нежизнеспособным. Такие изменения могут нарушить огромное количество "устаревшего" кода, который компилятор может доказать как действительный. Усилия, требующиеся для очистки базы кода, гораздо более целесообразны, когда большинство ошибок являются фактическими ошибками, а не новой версией компилятора.
unsigned char buff[]={0xff, 0x00, 0x1f};
это потерпит неудачу при сужающем преобразовании на основе типов, так как 0xff
имеет тип int
и такой код очень распространен.
Если бы такой код требовал бессмысленной модификации int
литералы к unsigned char
литералы, шансы, что развертка закончилась бы тем, что мы установили флаг, чтобы сказать компилятору заткнуться о глупой ошибке.
Сужение - это то, что компилятор знает только для встроенных типов. Определяемое пользователем неявное преобразование не может быть помечено как сужение или нет.
Во-первых, нельзя допускать, чтобы сужающиеся преобразования были неявными. (К сожалению, это было необходимо для совместимости с Си. Это было несколько исправлено с
{}
инициализация, запрещающая сужение для встроенных типов.)
Учитывая это, имеет смысл, что в правилах перегрузки не стоит упоминать этот особый случай. Это может быть случайным удобством, но это не так уж и ценно. IMO в целом лучше иметь меньше факторов, участвующих в разрешении перегрузки, и отклонять больше вещей как неоднозначные, заставляя программиста явно разрешать такие вещи.
Кроме того, double to float - это сужающее преобразование, когда double не является константным выражением или если 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};
^