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

时间:2017-02-19 20:56:03

标签: c++ c++11

摘自标准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。目的是禁止编译器提供的操作。例如,使类不可复制或不可移动。

然而,在这种情况下,意图似乎是意图的记录。这是正确的吗?是否有其他情况,在非成员函数上使用=delete是可取的,更可取或不可避免的?

1 个答案:

答案 0 :(得分:34)

我知道显式delete免费功能有两个一般原因:拒绝不需要的隐式转换,并为用户提供更好的错误体验。

拒绝不受欢迎的隐式转化

const的一个有用功能是临时工具可以绑定到const的引用。所以这有效:

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

临时42绑定到函数的引用参数,其生命周期与该引用参数相关联。

现在,考虑std::cref()。目标是将reference_wrapper传递到某个地方,因此我们需要基础参考来保持活力。如果我们刚刚过载:

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

然后我可以写std::cref(42)。这样可以正常工作,我会找回std::reference_wrapper<const int> - 除非是悬挂参考。这段代码没有办法可行。

为了解决这个明显的错误,我们也有了这个重载:

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

也就是说,我们明确删除(或定义为已删除)带有任何右值的重载。现在,在进行重载解析时,当我传入一个右值时,这个第二个重载是首选,并且该重载是错误的,并且编译器会告诉我们我们的错误(愚蠢的我,我不能做{{1}而不是我不得不花费几个小时与gdb试图找出我没有对象的原因。

标准库中的其他示例是:

为用户提供更好的诊断

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

cref(42)

我尝试用非整数类型调用它:

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

根据需要失败。但是你得到的错误并不是非常有意义的。特别是如果还有其他重载。使用Concepts,这会更好 - 希望,但不一定。

但如果我添加converse显式删除了重载:

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

这更明确,更直接。特别是如果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 的作者提供了一个注释,说明为什么这很重要。基本上,这仍然是SFINAE友好的,同时还有foo直接指示失败的好处(而如果我们只是static_assert,我们会得到更明确的信息,但我们会这样做失去SFINAE友好)。

事实上,N4186的动机是将该评论作为代码本身的一部分(尽管该提案被拒绝)。该论文的例子是:

static_assert

标准库中的一个示例是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()