std::unique_ptr
可以仅管理指针,还是也可以管理奇特的指针(类似指针的类型)?我认为“花式指针也是如此”,因为对于文件/套接字等资源,花式指针可能是一个很好的工具。似乎没有理由
std::unique_ptr
不应该支持他们。
在
std::unique_ptr
(GCC)中有不同的方法来测试托管指针的空性:
~unique_ptr()
中:if (__ptr != nullptr)
(代码)reset()
中:if (__old_p)
(代码)operator bool()
中:return get() == pointer() ? false : true;
(代码)问题是他们对指针类型有不同的要求:
bool operator ==(pointer, nullptr_t)
operator bool()
bool operator ==(pointer, pointer)
不一致的用法意味着所有这些都是必需的,而其中一个在语义上就足够了。
#include <memory>
using namespace std;
class File {
FILE * pf_ = nullptr;
public:
File() = default;
File(const char * pathname) { /*...*/ };
void Close() { /*...*/ };
// All are needed to make std::unique_ptr work
explicit operator bool() const { return pf_ == nullptr; }
bool operator ==(nullptr_t) const { return pf_ == nullptr; }
bool operator ==(File) const { /*...*/ return true; };
};
struct FileCloser {
using pointer = File;
void operator ()(File p) const { p.Close(); }
};
int main() {
using FilePtr = unique_ptr<File, FileCloser>;
FilePtr p(File("./abc"));
if(p)
p.reset();
}
https://godbolt.org/z/fjPjWzzhW
恕我直言,
std::unique_ptr
应该要求指针类型的最小值。一种解决方案是添加静态成员,例如__test_p
,并用它来一致地测试空性:
template<typename T, typename D = std::default_delete<T>>
class unique_ptr
{
// ...
private:
static constexpr bool __test_p(pointer p) noexcept {
if constexpr (is_convertible_v<pointer, bool>)
return bool(p);
else if constexpr (requires {{ p != nullptr} -> convertible_to<bool>; })
return p != nullptr;
else
return p != pointer();
}
// ...
};
std::unique_ptr<T, Deleter>
的要求是:
Deleter
必须是 FunctionObject 或对 FunctionObject 的左值引用或对函数的左值引用,可使用类型为 unique_ptr<T, Deleter>::pointer
的参数进行调用。
std::remove_reference_t<Deleter>::pointer
,如果存在,必须满足NullablePointer。
除其他事项外,NullablePointer 必须满足您声称等效的所有三个要求,并且它们必须彼此一致。
不一致的用法意味着所有这些都是必需的,而其中一个在语义上就足够了。
您无法从
bool operator ==(pointer, std::nullptr_t)
合成 bool operator ==(pointer, pointer)
,反之亦然。不相等的非空指针必须是可区分的。
拥有“冗余”操作是有利于约束的用户与约束的实现者的权衡,这并不是“错误”。
虽然
operator bool
确实必须等同于 ptr != nullptr
,但拥有 operator bool
更符合人体工程学,因为这允许 if(ptr)
等