shared_ptr 不能为空?
shared_ptr that cannot be null?
使用 std::shared_ptr
表示共享所有权和可选性(可能为空)。
我发现自己处于这样一种情况,我只想在我的代码中表达共享所有权,而没有任何选择权。当使用 shared_ptr
作为函数参数时,我必须让函数检查它是否为 consistent/safe.
在许多情况下,传递引用而不是当然是一种选择,但有时我也想转让所有权,因为可以使用 shared_ptr
。
是否有 class 来替换 shared_ptr
而不可能为 null,一些约定来处理这个问题,或者我的问题没有多大意义?
您正在请求 not_null
包装器 class。幸运的是,C++ 专家 guideline and there are already example implementations - like this one 已经解决了您的问题。搜索 not_null
class 模板。
您可以围绕 std::shared_ptr
编写一个包装器,它只允许从 non-null:
创建
#include <memory>
#include <cassert>
template <typename T>
class shared_reference
{
std::shared_ptr<T> m_ptr;
shared_reference(T* value) :m_ptr(value) { assert(value != nullptr); }
public:
shared_reference(const shared_reference&) = default;
shared_reference(shared_reference&&) = default;
~shared_reference() = default;
T* operator->() { return m_ptr.get(); }
const T* operator->() const { return m_ptr.get(); }
T& operator*() { return *m_ptr.get(); }
const T& operator*() const { return *m_ptr.get(); }
template <typename XT, typename...XTypes>
friend shared_reference<XT> make_shared_reference(XTypes&&...args);
};
template <typename T, typename...Types>
shared_reference<T> make_shared_reference(Types&&...args)
{
return shared_reference<T>(new T(std::forward<Types>(args)...));
}
请注意还缺少 operator=
。你绝对应该添加它。
你可以这样使用它:
#include <iostream>
using std::cout;
using std::endl;
struct test
{
int m_x;
test(int x) :m_x(x) { cout << "test("<<m_x<<")" << endl; }
test(const test& t) :m_x(t.m_x) { cout << "test(const test& " << m_x << ")" << endl; }
test(test&& t) :m_x(std::move(t.m_x)) { cout << "test(test&& " << m_x << ")" << endl; }
test& operator=(int x) { m_x = x; cout << "test::operator=(" << m_x << ")" << endl; return *this;}
test& operator=(const test& t) { m_x = t.m_x; cout << "test::operator=(const test& " << m_x << ")" << endl; return *this;}
test& operator=(test&& t) { m_x = std::move(t.m_x); cout << "test::operator=(test&& " << m_x << ")" << endl; return *this;}
~test() { cout << "~test(" << m_x << ")" << endl; }
};
#include <string>
int main() {
{
auto ref = make_shared_reference<test>(1);
auto ref2 = ref;
*ref2 = test(5);
}
{
test o(2);
auto ref = make_shared_reference<test>(std::move(o));
}
//Invalid case
//{
// test& a = *(test*)nullptr;
// auto ref = make_shared_reference<test>(a);
//}
}
输出:
test(1)
test(5)
test::operator=(test&& 5)
~test(5)
~test(5)
test(2)
test(test&& 2)
~test(2)
~test(2)
我希望我没有忘记任何可能导致未定义行为的内容。
查看 GSL's not_null
class 后,它调用 std::terminate()
而不是 abort()
;
我是这样实现的:
template <typename T>
class NonNull : public std::shared_ptr<T> {
typedef std::shared_ptr<T> super;
public:
inline NonNull()
: super(new T())
{
if ( ! super::get()) {
abort(); // Out of memory.
}
}
inline explicit NonNull(T *ptr)
: super(ptr)
{
if ( ! super::get()) {
abort(); // Input was null.
}
}
}
基本上,迫使我们构造 T
类型的 class。
用法:
// Directly is a `std::shared_ptr` type:
NonNull<MyClass> myVariable;
// Unlike:
gsl::not_null<std::shared_ptr<MyClass > > myVariable;
使用 std::shared_ptr
表示共享所有权和可选性(可能为空)。
我发现自己处于这样一种情况,我只想在我的代码中表达共享所有权,而没有任何选择权。当使用 shared_ptr
作为函数参数时,我必须让函数检查它是否为 consistent/safe.
在许多情况下,传递引用而不是当然是一种选择,但有时我也想转让所有权,因为可以使用 shared_ptr
。
是否有 class 来替换 shared_ptr
而不可能为 null,一些约定来处理这个问题,或者我的问题没有多大意义?
您正在请求 not_null
包装器 class。幸运的是,C++ 专家 guideline and there are already example implementations - like this one 已经解决了您的问题。搜索 not_null
class 模板。
您可以围绕 std::shared_ptr
编写一个包装器,它只允许从 non-null:
#include <memory>
#include <cassert>
template <typename T>
class shared_reference
{
std::shared_ptr<T> m_ptr;
shared_reference(T* value) :m_ptr(value) { assert(value != nullptr); }
public:
shared_reference(const shared_reference&) = default;
shared_reference(shared_reference&&) = default;
~shared_reference() = default;
T* operator->() { return m_ptr.get(); }
const T* operator->() const { return m_ptr.get(); }
T& operator*() { return *m_ptr.get(); }
const T& operator*() const { return *m_ptr.get(); }
template <typename XT, typename...XTypes>
friend shared_reference<XT> make_shared_reference(XTypes&&...args);
};
template <typename T, typename...Types>
shared_reference<T> make_shared_reference(Types&&...args)
{
return shared_reference<T>(new T(std::forward<Types>(args)...));
}
请注意还缺少 operator=
。你绝对应该添加它。
你可以这样使用它:
#include <iostream>
using std::cout;
using std::endl;
struct test
{
int m_x;
test(int x) :m_x(x) { cout << "test("<<m_x<<")" << endl; }
test(const test& t) :m_x(t.m_x) { cout << "test(const test& " << m_x << ")" << endl; }
test(test&& t) :m_x(std::move(t.m_x)) { cout << "test(test&& " << m_x << ")" << endl; }
test& operator=(int x) { m_x = x; cout << "test::operator=(" << m_x << ")" << endl; return *this;}
test& operator=(const test& t) { m_x = t.m_x; cout << "test::operator=(const test& " << m_x << ")" << endl; return *this;}
test& operator=(test&& t) { m_x = std::move(t.m_x); cout << "test::operator=(test&& " << m_x << ")" << endl; return *this;}
~test() { cout << "~test(" << m_x << ")" << endl; }
};
#include <string>
int main() {
{
auto ref = make_shared_reference<test>(1);
auto ref2 = ref;
*ref2 = test(5);
}
{
test o(2);
auto ref = make_shared_reference<test>(std::move(o));
}
//Invalid case
//{
// test& a = *(test*)nullptr;
// auto ref = make_shared_reference<test>(a);
//}
}
输出:
test(1)
test(5)
test::operator=(test&& 5)
~test(5)
~test(5)
test(2)
test(test&& 2)
~test(2)
~test(2)
我希望我没有忘记任何可能导致未定义行为的内容。
查看 GSL's not_null
class 后,它调用 std::terminate()
而不是 abort()
;
我是这样实现的:
template <typename T>
class NonNull : public std::shared_ptr<T> {
typedef std::shared_ptr<T> super;
public:
inline NonNull()
: super(new T())
{
if ( ! super::get()) {
abort(); // Out of memory.
}
}
inline explicit NonNull(T *ptr)
: super(ptr)
{
if ( ! super::get()) {
abort(); // Input was null.
}
}
}
基本上,迫使我们构造 T
类型的 class。
用法:
// Directly is a `std::shared_ptr` type:
NonNull<MyClass> myVariable;
// Unlike:
gsl::not_null<std::shared_ptr<MyClass > > myVariable;