Почему назначение указателя функции работает в прямом назначении, но не в условном операторе
(В этом примере не использовались #include, скомпилированные в MacOS10.14, Eclipse IDE, с g++, параметры -O0 -g3 -Wall -c -fmessage-length = 0)
Предполагая это объявление переменной:
int (*fun)(int);
Это не скомпилируется с "недопустимой перегрузкой std :: toupper и std :: tolower".
fun = (1 ? std::toupper : std::tolower); // ERROR, invalid overload
И это компилируется нормально:
if (1) {
fun = std::toupper; // OK
}
else {
fun = std::tolower; // OK
}
Ответы
Ответ 1
std::toupper
(1 и 2) и std::tolower
(1 и 2) перегружены. При определении общего типа между ними для условного оператора (до присвоения chr2fun
), какую перегрузку следует использовать, невозможно определить.
Вы можете использовать static_cast
чтобы указать, какой из них следует учитывать. (Предполагается, что принудительное разрешение перегрузки происходит сначала соответственно, затем проблема определения общего типа исчезает.)
static_cast
также может использоваться для устранения неоднозначности перегрузок функций путем выполнения преобразования функции в указатель в конкретный тип
например
chr2fun = (str2modus == STR2UP ? static_cast<int(*)(int)>(std::toupper)
: static_cast<int(*)(int)>(std::tolower));
Во втором случае chr2fun
назначается напрямую; тип chr2fun
является явным, и правильная перегрузка будет выбрана в разрешении перегрузки.
(акцент мой)
Во всех этих контекстах функция, выбранная из набора перегрузки, является функцией, тип которой соответствует указателю на функцию, ссылке на функцию или указателю на тип функции-члена, ожидаемый целью: объект или ссылка инициализируются, левая рука сторона присваивания, параметр функции или оператора, тип возвращаемого значения функции, целевой тип приведения или тип параметра шаблона соответственно.
Ответ 2
В первом случае компилятор блокируется, даже не добравшись до назначения. Упрощенное выражение:
(true ? std::toupper : std::tolower)
Не удастся скомпилировать, если имеется несколько перегрузок присутствующего toupper
/tolower
. Это связано с тем, что тип возвращаемого тернарного оператора должен быть установлен исключительно на основе типов 2-го и 3-го аргумента, не обращая внимания на контекст, в котором используется его результат.
Забавно, даже если один из этих аргументов не является перегруженной функцией, этого все же недостаточно. Причины этого менее очевидны и имеют большее отношение к правилам разрешения 1перегрузки и их применению. Приведение - это точно одна из семи возможностей его запуска, а определение целевого типа троичных операторов само по себе не является.
В случае прямого присвоения, правые стороны назначения должны соответствовать левым, и поэтому нет никакой двусмысленности.
В любом случае, как указывает @Caleth, согласно 16.5.4.2.1.6, этот код имеет неопределенное поведение.
1 Ссылка C++ содержит неверный параграф стандарта C++. [over.over] на самом деле 12.4.
Ответ 3
Этот фрагмент прекрасно компилируется с gcc 9.1.
#include <cctype>
int chr2fun(bool str2modus) {
const bool STR2UP = true;
int (*chr2fun)(int);
if (str2modus == STR2UP) {
chr2fun = std::toupper;
} else {
chr2fun = std::tolower;
}
chr2fun = (str2modus == STR2UP ? std::toupper : std::tolower);
}
На какой платформе и с каким компилятором вы получаете ошибку?