Почему это назначение указателя функции работает при непосредственном назначении, а не с условным оператором? - PullRequest
26 голосов
/ 04 июля 2019

(для этого примера не использовались никакие # 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
}

Ответы [ 3 ]

29 голосов
/ 04 июля 2019

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));

Для 2-го случая chr2fun назначается напрямую; тип chr2fun является явным, и правильная перегрузка будет выбрана в разрешении перегрузки .

(выделено мое)

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

11 голосов
/ 04 июля 2019

В первом случае компилятор блокируется, прежде чем даже получить назначение.Упрощенное выражение:

(true ? std::toupper : std::tolower)

Не удастся скомпилировать, если имеется несколько перегрузок toupper / tolower.Это связано с тем, что тип возвращаемого значения троичного оператора должен быть установлен исключительно на основе типов 2-го и 3-го аргумента, без учета контекста, в котором используется его результат.

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

В случае прямого присваивания правые стороны присваивания должны соответствовать lhs, и поэтомунет двусмысленности.

В любом случае , как указывает @Caleth, согласно 16.5.4.2.1.6 , этот код имеет неопределенное поведение.


1 Ссылка на C ++ содержит неверный абзац стандарта C ++.[over.over] на самом деле 12.4.

0 голосов
/ 04 июля 2019

Этот фрагмент прекрасно компилируется с 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);
}

На какой платформе и с каким компилятором вы получаете сообщение об ошибке?

...