摘自标准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
是可取的,更可取或不可避免的?
答案 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试图找出我没有对象的原因。
标准库中的其他示例是:
std::as_const()
std::addressof()
std::regex_match()
和std::regex_search()
(#6接受左值字符串,#7拒绝右值字符串)。另一类示例可能是为受约束的函数提供更好的诊断。让我们说我有一个仅对整数类型有意义的函数:
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()
。