简体   繁体   English

在非成员函数上使用delete有什么意义?

[英]What is the point of using delete on a non-member function?

Excerpt from the Standard 20.12 [function.objects] : 摘自标准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;

I am used to seeing =delete in the context of member functions. 我习惯在成员函数的上下文中看到=delete The intention is to prohibit an operation that was supplied by the compiler. 目的是禁止编译器提供的操作。 For example, to make a class non-copyable or non-movable. 例如,使类不可复制或不可移动。

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? 是否有其他情况在非成员函数上使用=delete是可取的,更可取或不可避免的?

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. 我知道明确delete自由函数有两个一般原因:拒绝不需要的隐式转换,并为用户提供更好的错误体验。


Rejecting undesired implicit conversions 拒绝不受欢迎的隐式转换

One useful feature of const is that temporaries can bind to references to const . const一个有用特性是temporaries可以绑定到const引用。 So this works: 这样可行:

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

That temporary 42 is bound to the reference parameter of the function, and its lifetime is tied to that reference parameter. 临时42绑定到函数的引用参数,其生命周期与该引用参数相关联。

Now, consider std::cref() .The goal is to pass through this reference_wrapper to somewhere, so we need the underlying reference to stay alive. 现在,考虑一下std::cref() 。目标是将这个reference_wrapper传递到某个地方,所以我们需要底层引用来保持活着。 If we just had this overload: 如果我们刚刚过载:

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

Then I could write std::cref(42) . 然后我可以写std::cref(42) That would work fine, I would get back a std::reference_wrapper<const int> - except it would be a dangling reference. 这样可以正常工作,我会回到std::reference_wrapper<const int> - 除了它将是一个悬空引用。 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;

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. 现在,在进行重载解析时,当我传入一个右值时,这个第二个重载是首选,并且该重载是错误的,并且编译器通知我们我们的错误(愚蠢的我,我不能做cref(42) !)而不是我不得不花费几个小时与gdb试图弄清楚为什么我没有对象。

Other examples in the standard library are: 标准库中的其他示例包括:

Better diagnostics for users 更好的用户诊断

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: 但是,如果我添加converse显式删除重载:

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

This is more explicit and direct. 这更加明确和直接。 Especially if the author of foo provides a comment indicating why this is important. 特别是如果foo的作者提供了一个注释,说明为什么这很重要。 Basically, this is still SFINAE friendly while also giving the benefit of a static_assert directly indicating failure (whereas if we just static_assert ed, we'd get a clearer message, but we'd lose SFINAE-friendliness). 基本上,这仍然是SFINAE友好的同时也给出了static_assert直接指示失败的好处(而如果我们只是static_assert ed,我们会得到更清晰的消息,但我们会失去SFINAE友好性)。

Indeed, the motivation of N4186 was to make that comment part of the code itself (though this proposal was rejected). 实际上, N4186的动机是将该评论作为代码本身的一部分(尽管该提案被拒绝)。 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; 

An example in the standard library is std::make_unique() for make_unique<U[N]> . 标准库中的一个示例是make_unique<U[N]> std::make_unique()

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

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