在非成员函数上使用delete有什么意义? [英] What is the point of using delete on a non-member function?

查看:179
本文介绍了在非成员函数上使用delete有什么意义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘录自标准20.12 [function.objects]:

Excerpt from the Standard 20.12 [function.objects] :

template <class T> reference_wrapper<T> ref(T&) noexcept;
template <class T> reference_wrapper<const T> cref(const T&) noexcept;
template <class T> void ref(const T&&) = delete;
template <class T> void cref(const T&&) = delete;

我习惯于看到 = delete 成员函数的上下文。目的是禁止编译器提供的操作。例如,使一个类不可复制或不可移动。

I am used to seeing =delete in the context of member functions. The intention is to prohibit an operation that was supplied by the compiler. For example, to make a class non-copyable or non-movable.

但是,在这种情况下,意图似乎是意图的文档。这是正确的吗?还有其他情况下,在非成员函数上使用 = delete 是可取的,可取的还是不可避免的?

In this context, however, the intention appears to be the documentation of the intent. Is this right? Are there any other cases where using a =delete on a non-member function is desirable, preferable or inevitable?

推荐答案

我知道有两个普遍的原因来明确地删除自由函数:拒绝不需要的隐式转换,并提供更好的隐式转换用户的错误体验。

There are two general reasons that I know of to explicitly delete free functions: to reject undesired implicit conversions, and to provide a better error experience for users.

const 是临时对象可以绑定到对 const 的引用。这样就可以了:

One useful feature of const is that temporaries can bind to references to const. So this works:

void foo(const int& );
foo(42); // ok

该临时 42 已绑定函数的参考参数,其生存期与该参考参数相关。

That temporary 42 is bound to the reference parameter of the function, and its lifetime is tied to that reference parameter.

现在,考虑 std :: cref()。目标是通过 reference_wrapper 到某个地方,因此我们需要基础引用才能保持生命。如果我们只是有这个重载:

Now, consider std::cref().The goal is to pass through this reference_wrapper to somewhere, so we need the underlying reference to stay alive. If we just had this overload:

template <class T>
reference_wrapper<const T> cref(const T&) noexcept;

然后我可以写 std :: cref(42)。那会很好,我会得到一个 std :: reference_wrapper< const int> -除非它是一个悬挂的引用。

Then I could write std::cref(42). That would work fine, I would get back a std::reference_wrapper<const int> - except it would be a dangling reference. There is no possible way for that code to ever work.

为了修复该明显的错误,我们也有这种重载:

In an effort to fix that obvious bug, we have this overload as well:

template <class T> void cref(const T&&) = delete;

也就是说,我们显式删除(或定义为Deleted)带有任何右值的重载。现在,当执行重载解析时,当我传递一个rvalue时,首选第二次重载,并且该重载格式不正确,并且编译器会通知我们我们的错误(对不起,我不能做 cref(42)!),而不是我不得不花几个小时与gdb试图弄清楚为什么我没有对象。

That is, we are explicitly deleting (or defining as deleted) an overloading taking any rvalue. Now, when doing overload resolution, this 2nd overload is preferred when I pass in an rvalue, and that overload is ill-formed, and the compiler informs us of our bug (silly me, I can't do cref(42)!) instead of me having to spend a few hours with gdb trying to figure out why I don't have an object.

标准库中的其他示例为:

Other examples in the standard library are:

  • std::as_const()
  • std::addressof()
  • std::regex_match() and std::regex_search() (#6 accepts lvalue strings, #7 rejects rvalue strings).

另一类示例可能是为约束函数提供更好的诊断。假设我有一个仅对整数类型有意义的函数:

A different class of example might be to provide a better diagnostic for constrained functions. Let's say I have a function that is only meaningful for integral types:

template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
void foo(T);

然后尝试用非整数类型调用它:

And I try to invoke it with a non-integral type:

foo(4.2); // error: no matching function

根据需要,此操作失败。但是,您得到的错误并不是很有意义。特别是如果还有其他超载。

This fails, as desired. But the error you get isn't super meaningful. Especially if there's other overloads. With Concepts, this'll be better - hopefully, but not necessarily.

但是,如果我添加相反地明确删除的重载:

But if I add the converse explicitly deleted overload:

template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
void foo(T);

template <typename T, std::enable_if_t<!std::is_integral_v<T>, int> = 0>
void foo(T) = delete;

foo(4.2); // error: use of deleted function

这更为明确和直接。尤其是如果 foo 的作者提供注释说明为什么这很重要。基本上,这仍然对SFINAE友好,同时还提供了直接表示失败的 static_assert 的好处(而如果我们只是 static_assert ed,我们会得到更清晰的信息,但是我们会失去对SFINAE的友好感。)

This is more explicit and direct. Especially if the author of foo provides a comment indicating why this is important. Basically, this is still SFINAE friendly while also giving the benefit of a static_assert directly indicating failure (whereas if we just static_asserted, we'd get a clearer message, but we'd lose SFINAE-friendliness).

实际上, N4186 旨在使该注释成为代码本身的一部分(尽管该建议被拒绝了) )。该文件中的示例为:

Indeed, the motivation of N4186 was to make that comment part of the code itself (though this proposal was rejected). The example in that paper was:


template <typename T>
enable_if_t<has_compatible_vector_size<simd_float, T>::value, simd_float>
operator+(simd_float, T);

template <typename T>
enable_if_t<!has_compatible_vector_size<simd_float, T>::value, simd_float>
operator+(simd_float, T) = delete;


标准库中的示例为 std :: make_unique() for make_unique< U [N]>

这篇关于在非成员函数上使用delete有什么意义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆