为什么这个函数指针赋值在直接赋值而不是条件运算符时有效?
Why does this function pointer assignment work when assigned directly but not with the conditional operator?
(此示例未使用#include,在 MacOS10.14、Eclipse IDE 上编译,使用 g++,选项 -O0 -g3 -Wall -c -fmessage-length=0)
假设这个变量声明:
int (*fun)(int);
使用 "invalid overload of std::toupper and std::tolower" 编译失败。
fun = (1 ? std::toupper : std::tolower); // ERROR, invalid overload
编译成功:
if (1) {
fun = std::toupper; // OK
}
else {
fun = std::tolower; // OK
}
std::toupper
(1 and 2) and std::tolower
(1 and 2) are overloaded. When determining the common type between them for the conditional operator(赋值给chr2fun
之前),无法确定应该使用哪个重载。
可以先用static_cast
to specify which one should be considered. (Presicely, to force the overload resolution分别happens,这样就省去了判断公共类型的麻烦。)
static_cast
may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
例如
chr2fun = (str2modus == STR2UP ? static_cast<int(*)(int)>(std::toupper)
: static_cast<int(*)(int)>(std::tolower));
对于第2种情况,直接赋值chr2fun
; chr2fun
的类型是显式的,将在 overload resolution.
中选择正确的重载
(强调我的)
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.
这个 snippet 使用 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);
}
您在什么平台上使用哪个编译器遇到错误?
在第一种情况下,编译器甚至在进行赋值之前就犹豫了。简化表达式:
(true ? std::toupper : std::tolower)
如果存在 toupper
/tolower
的多个重载,将无法编译。这是因为三元运算符的 return 类型必须完全基于第二个和第三个参数的类型来建立,而无需查看使用它的结果的上下文。
有趣的是,即使这些参数之一 不是 重载函数,这仍然不够。其原因不太明显,更多地与 overload resolution1 规则及其适用位置有关。强制转换恰好是触发它的七种可能性之一,而确定三元运算符本身的目标类型则不是。
在直接赋值的情况下,赋值的rhs必须和lhs相符,所以不会有歧义。
无论哪种方式,正如@Caleth 所指出的,根据16.5.4.2.1.6,此代码具有未指定的行为。
1C++ 参考有一个不正确的 C++ 标准段落。 [over.over] 实际上是 12.4。
(此示例未使用#include,在 MacOS10.14、Eclipse IDE 上编译,使用 g++,选项 -O0 -g3 -Wall -c -fmessage-length=0)
假设这个变量声明:
int (*fun)(int);
使用 "invalid overload of std::toupper and std::tolower" 编译失败。
fun = (1 ? std::toupper : std::tolower); // ERROR, invalid overload
编译成功:
if (1) {
fun = std::toupper; // OK
}
else {
fun = std::tolower; // OK
}
std::toupper
(1 and 2) and std::tolower
(1 and 2) are overloaded. When determining the common type between them for the conditional operator(赋值给chr2fun
之前),无法确定应该使用哪个重载。
可以先用static_cast
to specify which one should be considered. (Presicely, to force the overload resolution分别happens,这样就省去了判断公共类型的麻烦。)
static_cast
may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
例如
chr2fun = (str2modus == STR2UP ? static_cast<int(*)(int)>(std::toupper)
: static_cast<int(*)(int)>(std::tolower));
对于第2种情况,直接赋值chr2fun
; chr2fun
的类型是显式的,将在 overload resolution.
(强调我的)
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.
这个 snippet 使用 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);
}
您在什么平台上使用哪个编译器遇到错误?
在第一种情况下,编译器甚至在进行赋值之前就犹豫了。简化表达式:
(true ? std::toupper : std::tolower)
如果存在 toupper
/tolower
的多个重载,将无法编译。这是因为三元运算符的 return 类型必须完全基于第二个和第三个参数的类型来建立,而无需查看使用它的结果的上下文。
有趣的是,即使这些参数之一 不是 重载函数,这仍然不够。其原因不太明显,更多地与 overload resolution1 规则及其适用位置有关。强制转换恰好是触发它的七种可能性之一,而确定三元运算符本身的目标类型则不是。
在直接赋值的情况下,赋值的rhs必须和lhs相符,所以不会有歧义。
无论哪种方式,正如@Caleth 所指出的,根据16.5.4.2.1.6,此代码具有未指定的行为。
1C++ 参考有一个不正确的 C++ 标准段落。 [over.over] 实际上是 12.4。