简体   繁体   English

Function 指针,它接受有和没有 noexcept

[英]Function pointer which accepts both with and without noexcept

I have some utility code that I've been using for years to safely call the ctype family of functions, it looks like this:我有一些实用程序代码,多年来我一直在使用这些代码来安全地调用 ctype 系列函数,它看起来像这样:

template<int (&F)(int)>
int safe_ctype(unsigned char c) {
    return F(c);
}

And is used like this:并且是这样使用的:

int r = safe_ctype<std::isspace>(ch);

The idea being that it handles the need to cast the input int to an unsigned value for you in order to prevent undefined behavior.这个想法是它为您处理将输入int转换为无符号值的需要,以防止未定义的行为。 The specifics of this function is somewhat irrelivant though.这个 function 的细节有点无关紧要。 Here's my question:这是我的问题:

Now that in C++17 and later, noexcept is part of the type system, this is a compile error!现在在 C++17 及更高版本中, noexcept是类型系统的一部分,这是一个编译错误! Because all of the ctype functions are now noexcept .因为所有 ctype 函数现在都是noexcept


EDIT : The above sentence is incorrect.编辑:上面的句子是不正确的。 the ctype family of functions are not noexcept . ctype 系列函数不是noexcept I was however getting a compiler error in gcc < 11.2.但是,我在 gcc < 11.2 中遇到编译器错误。 https://godbolt.org/z/cTq94q5xE https://godbolt.org/z/cTq94q5xE

The code works as expected (despite being technically not allowed due to these functions not being addressable) with the latest versions of all 3 major compilers.该代码可以在所有 3 个主要编译器的最新版本中按预期工作(尽管由于这些函数不可寻址,技术上不允许这样做)。


I can of course change my function to look like this:我当然可以将我的 function 更改为如下所示:

template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
    return F(c);
}

But now it doesn't work when compiled as C++11 or C++14.但是现在编译为 C++11 或 C++14 时它不起作用。 So I end up having to do something like this:所以我最终不得不做这样的事情:

#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

Which is getting increasingly complex for such a simple task.对于这样一个简单的任务,这变得越来越复杂。 So is there a way to make the function pointer:那么有没有办法制作 function 指针:

  1. valid for C++11 - C++20适用于 C++11 - C++20
  2. Accept both noexcept and non-noexcept when in C++17+在 C++17+ 中同时接受 noexcept 和 non-noexcept

? ?

I tried doing something like this:我试着做这样的事情:

template<class F>
int safe_ctype(unsigned char c) noexcept {
    return F(c);
}

In the hopes that it would accept "anything", but sadly, no go.希望它会接受“任何东西”,但遗憾的是,没有 go。

Thoughts?想法?

Now that in C++17 and later, noexcept is part of the type system, this is a compile error.现在在 C++17 及更高版本中, noexcept 是类型系统的一部分,这是一个编译错误。 Because all of the ctype functions are now noexcept.因为现在所有的 ctype 函数都是 noexcept。

It is not a compile error.这不是编译错误。 Pointers to noexcept functions are implicitly convertible to pointers to potentially throwing functions, and thus the template accepting a pointer to potentially throwing functions works with both potentially throwing and noexcept functions.指向 noexcept 函数的指针可以隐式转换为指向潜在抛出函数的指针,因此接受指向潜在抛出函数的指针的模板适用于潜在抛出函数和 noexcept 函数。 Only caveat is that the noexceptedness information is lost and might not be used for optimisation purposes.唯一需要注意的是,noexceptedness 信息会丢失,并且可能不会用于优化目的。

Hence, the original solution satisfies both points 1. and 2.因此,原始解同时满足第 1 点和第 2 点。


Another problem pointed out in the comments is that the standard library functions ( std::isspace ) that you intend to use are not designated "addressable".评论中指出的另一个问题是您打算使用的标准库函数( std::isspace )未指定为“可寻址”。 Hence the behaviour of the program is unspecified (possibly ill-formed) due to forming a pointer to them.因此,由于形成了指向它们的指针,程序的行为是未指定的(可能是格式错误的)。

To wrap such callable, you could use a lambda instead of a function pointer.要包装此类可调用对象,您可以使用 lambda 而不是 function 指针。 But that makes the template itself obsolete since you can change the argument type of the lambda directly:但这会使模板本身过时,因为您可以直接更改 lambda 的参数类型:

auto safe_isspace = [](unsigned char c){ return std::isspace(c); };
int r = safe_isspace(ch);

Though we no longer need to pass this into a template, so the same can be achieved with a plain function:虽然我们不再需要将其传递到模板中,但同样可以使用普通的 function 来实现:

int // or bool?
safe_isspace(unsigned char c) noexcept // ...

Since this involves a bit of identical boilerplate for multiple functions, this is a good candidate for meta-programming.由于这涉及多个功能的一些相同样板,因此这是元编程的良好候选者。

Because all of the ctype functions are now noexcept.因为现在所有的 ctype 函数都是 noexcept。

This is untrue.这是不真实的。 C++17 did not add noexcept to any C-library functions accessed through the C++ c* headers. C++17 没有向通过 C++ c*头文件访问的任何 C 库函数添加noexcept You can see here that all of the C++ function declarations do not contain noexcept .您可以在此处看到所有 C++ function 声明不包含noexcept And a standard library implementation is not allowed to make non- noexcept functions noexcept .并且不允许标准库实现将非noexcept函数noexcept

Secondly, even if it were noexcept , a noexcept function pointer can be converted into a throwing function pointer (but not the other way around).其次,即使它是noexcept ,也可以将noexcept function 指针转换为抛出的 function 指针(但不能反过来)。 So your code compiles .所以你的代码编译

But most importantly, C++20 makes it clear that you are not allowed to get function pointers for any C++ standard library function unless it is specifically stated to be "addressable".但最重要的是,C++20 清楚地表明,除非特别说明,否则不允许为任何 C++ 标准库 function 获取 function 指针。 And there are very few addressable functions in the C++ standard library. C++标准库中可寻址的函数很少。

So in C++20, your code will yield UB.所以在 C++20 中,你的代码会产生 UB。 You're just going to have to write wrappers for the cctype functions if you want your code to work across all language versions.如果您希望您的代码适用于所有语言版本,您只需要为 cctype 函数编写包装器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM