摘自标准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
是可取的、可取的或不可避免的?
发布于 2017-02-20 05:19:25
据我所知,显式delete
自由函数有两个一般原因:拒绝不需要的隐式转换,以及为用户提供更好的错误体验。
拒绝不需要的隐式转换
const
的一个有用特性是,临时文件可以绑定到对const
的引用。所以这是可行的:
void foo(const int& );
foo(42); // ok
现在,考虑到std::cref()
.The的目标是通过这个reference_wrapper
传递到某个地方,所以我们需要底层引用来保持活动状态。如果我们只有这个重载:
template <class T>
reference_wrapper<const T> cref(const T&) noexcept;
然后我就可以编写std::cref(42)
了。这将很好地工作,我将得到一个std::reference_wrapper<const int>
-除了它将是一个悬空的引用。这段代码根本不可能正常工作。
为了修复这个明显的bug,我们也有这个重载:
template <class T> void cref(const T&&) = delete;
也就是说,我们显式地删除(或定义为已删除)一个重载,该重载采用任何r值。现在,在执行重载解析时,当我传入一个r值时,第二个重载是首选的,而这个重载是错误的,编译器会通知我们我们的错误(愚蠢的我,我不能做cref(42)
!)而不是让我花几个小时在gdb上试图弄清楚为什么我没有对象。
标准库中的其他示例包括:
为用户提供更好的诊断
另一类示例可能是为受约束的函数提供更好的诊断。假设我有一个只对整型有意义的函数:
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
void foo(T);
我试着用非整型调用它:
foo(4.2); // error: no matching function
正如所希望的那样,该操作失败了。但是你得到的错误并不是特别有意义。尤其是在有其他重载的情况下。有了Concepts,这会更好--希望如此,但不一定如此。
但如果我添加反向显式删除重载:
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友好)。
事实上,N4186的动机是使该注释成为代码本身的一部分(尽管此提议被拒绝)。那篇论文中的例子是:
删除template enable_if_t::value,simd_float> operator+(simd_float,T);template enable_if_t operator+(simd_float,T) =删除;
标准库中的一个示例是用于make_unique<U[N]>
的std::make_unique()
。
https://stackoverflow.com/questions/42332777
复制相似问题