Why does this function pointer assignment work whe

2020-05-31 06:27发布

问题:

(No #include's were used for this example, compiled on MacOS10.14, Eclipse IDE, with g++, options -O0 -g3 -Wall -c -fmessage-length=0)

Assuming this variable declaration:

int (*fun)(int);

This fails to compile with "invalid overload of std::toupper and std::tolower".

fun = (1 ? std::toupper : std::tolower);   // ERROR, invalid overload

And this compiles OK:

if (1) {
    fun = std::toupper;   // OK
}
else {
    fun = std::tolower;   // OK
}

回答1:

std::toupper(1 and 2) and std::tolower(1 and 2) are overloaded. When determining the common type between them for the conditional operator (before the assignment to chr2fun), which overloading should be used can't be determined.

You can use static_cast to specify which one should be considered. (Presicely, to force the overload resolution happens at first respectively, then the trouble in determining the common type disappears.)

static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type

e.g.

chr2fun = (str2modus == STR2UP ? static_cast<int(*)(int)>(std::toupper) 
                               : static_cast<int(*)(int)>(std::tolower));

For the 2nd case, chr2fun is assigned directly; the type of chr2fun is explicit and the correct overloading would be selected in overload resolution.

(emphasis mine)

In all these contexts, the function selected from the overload set is the function whose type matches the pointer to function, reference to function, or pointer to member function type that is expected by target: the object or reference being initialized, the left-hand side of the assignment, function or operator parameter, the return type of a function, the target type of a cast, or the type of the template parameter, respectively.



回答2:

In the first case, the compiler balks before even getting to the assignment. A simplified expression:

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

Will fail to compile if there are multiple overloads of toupper/tolower present. This is because the ternary operator's return type must be established based solely on the types of 2nd and 3rd argument, without ever looking at the context where the result of it is used.

Funny enough, even if one of those arguments isn't an overloaded function, that's still not enough. Reasons for that are less obvious, and have more to do with overload resolution1 rules and where they apply. A cast is precisely one of seven possibilities to trigger it, and determining the target type of ternary operators itself isn't.

In the case of direct assignment, the rhs of assignment must fit the lhs, and so there's no ambiguity.

Either way, as pointed out by @Caleth, according to 16.5.4.2.1.6, this code has unspecified behaviour.


1The C++ Reference has an incorrect C++ Standard paragraph. [over.over] is actually 12.4.



回答3:

This snippet compiles fine with 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);
}

On what platform and with which compiler do you get the error?