C++20的”addressing restriction”有什么作用?

huangapple go评论65阅读模式
英文:

What is the C++20 "addressing restriction" good for?

问题

根据cppreference.com,C++20引入了"addressing restriction"("地址限制")用于标准库函数:

地址限制

如果明确或隐式地尝试形成指向标准库函数或标准库函数模板实例的指针、引用(对于自由函数和静态成员函数)或指向成员函数的指针(对于非静态成员函数),并且未指定其为可寻址函数(见下文),则C++程序的行为是未指定的(可能是不合法的)。

以下代码在C++17中是良好定义的,但自C++20以来会导致未指定的行为,甚至可能编译失败:

#include <cmath>
#include <memory>
 
int main()
{
    auto fptr0 = &std::betaf; // 通过一元运算符&
    auto fptr1 = std::addressof(std::betal); // 通过std::addressof
    auto fptr2 = std::riemann_zetaf; // 通过函数指针的隐式转换
    auto &fref = std::riemann_zetal; // 形成引用
}

为什么引入这一改变?

尤其关于语言的向后兼容性,这似乎是一个会破坏许多现有代码的变更。这一变更提供了什么好处,以至于值得引入这种破坏性的变化呢?

英文:

According to cppreference.com, C++20 introduces the "addressing restriction" for standard library functions:

> #### Addressing restriction
>
> The behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer, reference (for free functions and static member functions) or pointer-to-member (for non-static member functions) to a standard library function or an instantiation of a standard library function template, unless it is designated an addressable function (see below).
>
> Following code was well-defined in C++17, but leads to unspecified behaviors and possibly fails to compile since C++20:
>
&gt; #include &lt;cmath&gt;
&gt; #include &lt;memory&gt;
&gt;
&gt; int main()
&gt; {
&gt; auto fptr0 = &amp;std::betaf; // by unary operator&amp;
&gt; auto fptr1 = std::addressof(std::betal); // by std::addressof
&gt; auto fptr2 = std::riemann_zetaf; // by function-to-pointer implicit conversion
&gt; auto &amp;fref = std::riemann_zetal; // forming a reference
&gt; }
&gt;

Why was this introduced?

Especially with regard to backwards compatibility of the language, this seems to be a change that breaks a lot of existing code. What benefit does this provide that makes worth such a breaking change?

答案1

得分: 7

只有代码部分:

It was never "well-defined", it just happened to work on your/some/most implementation. (CppReference is wrong here)

Buried in the introduction of the library section of the standard is a description of what implementors must provide. For (say) `std::riemann_zetal`, what's required is that there's a "thing" that can be called using function-call syntax, that takes a single `float` parameter, and returns something that can be converted to a `float`.

So, this is a perfectly legal implementation:

template <typename FP>
FP ZETA(FP f, int flags = 0) { /* code here */ } 

#define riemann_zetaf(x)  ZETA<float>(x, 23)

But, you can't take the address of `riemann_zetaf` because there is no such beast.

Note that even without the macro, you have no guarantee that `addressof(riemann_zetal)` would return a function that takes exactly one parameter.  The only guarantee you have is that you can call it with one parameter.

If you need something that you can take the address of, define it yourself:

float riemann_zetal (float x) { return std::riemann_zetal(x); }
英文:

It was never "well-defined", it just happened to work on your/some/most implementation. (CppReference is wrong here)

Buried in the introduction of the library section of the standard is a description of what implementors must provide. For (say) std:: riemann_zetal, what's required is that there's a "thing" that can be called using function-call syntax, that takes a single float parameter, and returns something that can be converted to a float.

So, this is a perfectly legal implementation:

template &lt;typename FP&gt;
FP ZETA(FP f, int flags = 0) { /* code here */ } 

#define riemann_zetaf(x)  ZETA&lt;float&gt;(x, 23)

But, you can't take the address of riemann_zetaf because there is no such beast.

Note that even without the macro, you have no guarantee that addressof(riemann_zetal) would return a function that takes exactly one parameter. The only guarantee you have is that you can call it with one parameter.

If you need something that you can take the address of, define it yourself:

float riemann_zetal (float x) { return std::riemann_zetal(x); }

and then take the address of that instead.

huangapple
  • 本文由 发表于 2023年4月11日 02:43:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979794.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定