接受有和没有 noexcept 的函数指针
Function pointer which accepts both with and without noexcept
我有一些实用程序代码,我多年来一直在使用它来安全地调用 ctype 系列函数,它看起来像这样:
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
并且是这样使用的:
int r = safe_ctype<std::isspace>(ch);
想法是它处理将输入 int
转换为无符号值的需要,以防止出现未定义的行为。不过,此功能的细节有些无关紧要。这是我的问题:
既然在C++17及以后的版本中,noexcept
是类型系统的一部分,这是一个编译错误!因为所有的 ctype 函数现在都是 noexcept
.
EDIT: 上面这句话是不正确的。 ctype 函数族是 not noexcept
。然而,我在 gcc < 11.2 中遇到编译器错误。 https://godbolt.org/z/cTq94q5xE
代码在所有 3 个主要编译器的最新版本中按预期工作(尽管由于这些函数不可寻址而在技术上不允许)。
我当然可以将函数更改为如下所示:
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
但现在编译为 C++11 或 C++14 时它不起作用。所以我最终不得不做这样的事情:
#if __cplusplus >= 201703L
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
#else
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
#endif
如此简单的任务却变得越来越复杂。那么有没有办法让函数指针:
- 适用于 C++11 - C++20
- 在 C++17+ 中接受 noexcept 和非 noexcept
?
我试过这样做:
template<class F>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
希望它能接受“任何东西”,但遗憾的是,没有接受。
想法?
Now that in C++17 and later, noexcept is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept.
不是编译错误。指向 noexcept 函数的指针可以隐式转换为指向潜在抛出函数的指针,因此接受指向潜在抛出函数的指针的模板适用于潜在抛出函数和 noexcept 函数。唯一需要注意的是,noexceptedness 信息会丢失并且可能不会用于优化目的。
因此,原解同时满足第1点和第2点
评论中指出的另一个问题是您打算使用的标准库函数(std::isspace
)未指定为“可寻址”。因此,由于形成指向它们的指针,程序的行为是未指定的(可能 ill-formed)。
要包装此类可调用对象,您可以使用 lambda 而不是函数指针。但这会使模板本身过时,因为您可以直接更改 lambda 的参数类型:
auto safe_isspace = [](unsigned char c){ return std::isspace(c); };
int r = safe_isspace(ch);
虽然我们不再需要将其传递到模板中,但可以使用普通函数实现相同的目的:
int // or bool?
safe_isspace(unsigned char c) noexcept // ...
由于这涉及多个函数的一些相同样板,因此这是 meta-programming 的一个很好的候选者。
Because all of the ctype functions are now noexcept.
这是不正确的。 C++17 没有将 noexcept
添加到通过 C++ c*
headers 访问的任何 C-library 函数。你可以see here所有的C++函数声明都不包含noexcept
。并且不允许使用标准库实现非 noexcept
函数 noexcept
.
其次,即使是noexcept
,一个noexcept
函数指针也可以转换成一个throwing函数指针(但反过来不行)。所以 your code compiles.
但最重要的是,C++20 清楚地表明,您 不允许 获取任何 C++ 标准库函数的函数指针,除非明确声明它是“可寻址的” ”。而且C++标准库中可寻址的函数非常少
因此在 C++20 中,您的代码将产生 UB。如果您希望您的代码适用于所有语言版本,您只需为 cctype 函数编写包装器。
我有一些实用程序代码,我多年来一直在使用它来安全地调用 ctype 系列函数,它看起来像这样:
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
并且是这样使用的:
int r = safe_ctype<std::isspace>(ch);
想法是它处理将输入 int
转换为无符号值的需要,以防止出现未定义的行为。不过,此功能的细节有些无关紧要。这是我的问题:
既然在C++17及以后的版本中,noexcept
是类型系统的一部分,这是一个编译错误!因为所有的 ctype 函数现在都是 noexcept
.
EDIT: 上面这句话是不正确的。 ctype 函数族是 not noexcept
。然而,我在 gcc < 11.2 中遇到编译器错误。 https://godbolt.org/z/cTq94q5xE
代码在所有 3 个主要编译器的最新版本中按预期工作(尽管由于这些函数不可寻址而在技术上不允许)。
我当然可以将函数更改为如下所示:
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
但现在编译为 C++11 或 C++14 时它不起作用。所以我最终不得不做这样的事情:
#if __cplusplus >= 201703L
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
#else
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
#endif
如此简单的任务却变得越来越复杂。那么有没有办法让函数指针:
- 适用于 C++11 - C++20
- 在 C++17+ 中接受 noexcept 和非 noexcept
?
我试过这样做:
template<class F>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
希望它能接受“任何东西”,但遗憾的是,没有接受。
想法?
Now that in C++17 and later, noexcept is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept.
不是编译错误。指向 noexcept 函数的指针可以隐式转换为指向潜在抛出函数的指针,因此接受指向潜在抛出函数的指针的模板适用于潜在抛出函数和 noexcept 函数。唯一需要注意的是,noexceptedness 信息会丢失并且可能不会用于优化目的。
因此,原解同时满足第1点和第2点
评论中指出的另一个问题是您打算使用的标准库函数(std::isspace
)未指定为“可寻址”。因此,由于形成指向它们的指针,程序的行为是未指定的(可能 ill-formed)。
要包装此类可调用对象,您可以使用 lambda 而不是函数指针。但这会使模板本身过时,因为您可以直接更改 lambda 的参数类型:
auto safe_isspace = [](unsigned char c){ return std::isspace(c); };
int r = safe_isspace(ch);
虽然我们不再需要将其传递到模板中,但可以使用普通函数实现相同的目的:
int // or bool?
safe_isspace(unsigned char c) noexcept // ...
由于这涉及多个函数的一些相同样板,因此这是 meta-programming 的一个很好的候选者。
Because all of the ctype functions are now noexcept.
这是不正确的。 C++17 没有将 noexcept
添加到通过 C++ c*
headers 访问的任何 C-library 函数。你可以see here所有的C++函数声明都不包含noexcept
。并且不允许使用标准库实现非 noexcept
函数 noexcept
.
其次,即使是noexcept
,一个noexcept
函数指针也可以转换成一个throwing函数指针(但反过来不行)。所以 your code compiles.
但最重要的是,C++20 清楚地表明,您 不允许 获取任何 C++ 标准库函数的函数指针,除非明确声明它是“可寻址的” ”。而且C++标准库中可寻址的函数非常少
因此在 C++20 中,您的代码将产生 UB。如果您希望您的代码适用于所有语言版本,您只需为 cctype 函数编写包装器。