[英]Nullable values in C++
I'm creating a database access layer in native C++, and I'm looking at ways to support NULL values.我正在用本机 C++ 创建一个数据库访问层,并且正在寻找支持 NULL 值的方法。 Here is what I have so far:
这是我到目前为止所拥有的:
class CNullValue
{
public:
static CNullValue Null()
{
static CNullValue nv;
return nv;
}
};
template<class T>
class CNullableT
{
public:
CNullableT(CNullValue &v) : m_Value(T()), m_IsNull(true)
{
}
CNullableT(T value) : m_Value(value), m_IsNull(false)
{
}
bool IsNull()
{
return m_IsNull;
}
T GetValue()
{
return m_Value;
}
private:
T m_Value;
bool m_IsNull;
};
This is how I'll have to define functions:这就是我必须定义函数的方式:
void StoredProc(int i, CNullableT<int> j)
{
...connect to database
...if j.IsNull pass null to database etc
}
And I call it like this:我这样称呼它:
sp.StoredProc(1, 2);
or或者
sp.StoredProc(3, CNullValue::Null());
I was just wondering if there was a better way than this.我只是想知道是否有比这更好的方法。 In particular I don't like the singleton-like object of CNullValue with the statics.
特别是我不喜欢带有静态的 CNullValue 的单例对象。 I'd prefer to just do
我宁愿做
sp.StoredProc(3, CNullValue);
or something similar.或类似的东西。 How do others solve this problem?
其他人如何解决这个问题?
Boost.Optional probably does what you need. Boost.Optional可能满足您的需求。
boost::none
takes the place of your CNullValue::Null()
. boost::none
取代了你的CNullValue::Null()
。 Since it's a value rather than a member function call, you can do using boost::none;
由于它是一个值而不是成员函数调用,因此您可以
using boost::none;
if you like, for brevity.如果你愿意,为了简洁。 It has a conversion to
bool
instead of IsNull
, and operator*
instead of GetValue
, so you'd do:它有一个转换为
bool
而不是IsNull
和operator*
而不是GetValue
,所以你会这样做:
void writeToDB(boost::optional<int> optional_int) {
if (optional_int) {
pass *optional_int to database;
} else {
pass null to database;
}
}
But what you've come up with is essentially the same design, I think.但我认为你想出的设计本质上是相同的。
EDIT1 : Improved with throw exception on "null" Value. EDIT1 :改进了“null”值抛出异常。 More fixes
更多修复
EDIT2 : An alternative version supporting nullable references can be found here . EDIT2 : 可以在此处找到支持可为空引用的替代版本。
If Boost.Optional
or std::optional
are not an option, in c++11 you can also take advantage of nullptr
and the nullptr_t
typedef to create a Nullable<T>
with pretty much same semantics as .NET one.如果
Boost.Optional
或std::optional
不是一个选项,在 c++11 中,您还可以利用nullptr
和nullptr_t
typedef 创建一个Nullable<T>
,其语义与 .NET 几乎相同。
#pragma once
#include <cstddef>
#include <stdexcept>
template <typename T>
class Nullable final
{
public:
Nullable();
Nullable(const T &value);
Nullable(std::nullptr_t nullpointer);
const Nullable<T> & operator=(const Nullable<T> &value);
const Nullable<T> & operator=(const T &value);
const Nullable<T> & operator=(std::nullptr_t nullpointer);
bool HasValue() const;
const T & GetValueOrDefault() const;
const T & GetValueOrDefault(const T &def) const;
bool TryGetValue(T &value) const;
T * operator->();
const T * operator->() const;
T & operator*();
const T & operator*() const;
public:
class NullableValue final
{
public:
friend class Nullable;
private:
NullableValue();
NullableValue(const T &value);
public:
NullableValue & operator=(const NullableValue &) = delete;
operator const T &() const;
const T & operator*() const;
const T * operator&() const;
// https://stackoverflow.com/questions/42183631/inability-to-overload-dot-operator-in-c
const T * operator->() const;
public:
template <typename T2>
friend bool operator==(const Nullable<T2> &op1, const Nullable<T2> &op2);
template <typename T2>
friend bool operator==(const Nullable<T2> &op, const T2 &value);
template <typename T2>
friend bool operator==(const T2 &value, const Nullable<T2> &op);
template <typename T2>
friend bool operator==(const Nullable<T2> &op, std::nullptr_t nullpointer);
template <typename T2>
friend bool operator!=(const Nullable<T2> &op1, const Nullable<T2> &op2);
template <typename T2>
friend bool operator!=(const Nullable<T2> &op, const T2 &value);
template <typename T2>
friend bool operator!=(const T2 &value, const Nullable<T2> &op);
template <typename T2>
friend bool operator==(std::nullptr_t nullpointer, const Nullable<T2> &op);
template <typename T2>
friend bool operator!=(const Nullable<T2> &op, std::nullptr_t nullpointer);
template <typename T2>
friend bool operator!=(std::nullptr_t nullpointer, const Nullable<T2> &op);
private:
void checkHasValue() const;
private:
bool m_hasValue;
T m_value;
};
public:
NullableValue Value;
};
template <typename T>
Nullable<T>::NullableValue::NullableValue()
: m_hasValue(false), m_value(T()) { }
template <typename T>
Nullable<T>::NullableValue::NullableValue(const T &value)
: m_hasValue(true), m_value(value) { }
template <typename T>
Nullable<T>::NullableValue::operator const T &() const
{
checkHasValue();
return m_value;
}
template <typename T>
const T & Nullable<T>::NullableValue::operator*() const
{
checkHasValue();
return m_value;
}
template <typename T>
const T * Nullable<T>::NullableValue::operator&() const
{
checkHasValue();
return &m_value;
}
template <typename T>
const T * Nullable<T>::NullableValue::operator->() const
{
checkHasValue();
return &m_value;
}
template <typename T>
void Nullable<T>::NullableValue::checkHasValue() const
{
if (!m_hasValue)
throw std::runtime_error("Nullable object must have a value");
}
template <typename T>
bool Nullable<T>::HasValue() const { return Value.m_hasValue; }
template <typename T>
const T & Nullable<T>::GetValueOrDefault() const
{
return Value.m_value;
}
template <typename T>
const T & Nullable<T>::GetValueOrDefault(const T &def) const
{
if (Value.m_hasValue)
return Value.m_value;
else
return def;
}
template <typename T>
bool Nullable<T>::TryGetValue(T &value) const
{
value = Value.m_value;
return Value.m_hasValue;
}
template <typename T>
Nullable<T>::Nullable() { }
template <typename T>
Nullable<T>::Nullable(std::nullptr_t nullpointer) { (void)nullpointer; }
template <typename T>
Nullable<T>::Nullable(const T &value)
: Value(value) { }
template <typename T2>
bool operator==(const Nullable<T2> &op1, const Nullable<T2> &op2)
{
if (op1.Value.m_hasValue != op2.Value.m_hasValue)
return false;
if (op1.Value.m_hasValue)
return op1.Value.m_value == op2.Value.m_value;
else
return true;
}
template <typename T2>
bool operator==(const Nullable<T2> &op, const T2 &value)
{
if (!op.Value.m_hasValue)
return false;
return op.Value.m_value == value;
}
template <typename T2>
bool operator==(const T2 &value, const Nullable<T2> &op)
{
if (!op.Value.m_hasValue)
return false;
return op.Value.m_value == value;
}
template <typename T2>
bool operator==(const Nullable<T2> &op, std::nullptr_t nullpointer)
{
(void)nullpointer;
return !op.Value.m_hasValue;
}
template <typename T2>
bool operator==(std::nullptr_t nullpointer, const Nullable<T2> &op)
{
(void)nullpointer;
return !op.Value.m_hasValue;
}
template <typename T2>
bool operator!=(const Nullable<T2> &op1, const Nullable<T2> &op2)
{
if (op1.Value.m_hasValue != op2.Value.m_hasValue)
return true;
if (op1.Value.m_hasValue)
return op1.Value.m_value != op2.Value.m_value;
else
return false;
}
template <typename T2>
bool operator!=(const Nullable<T2> &op, const T2 &value)
{
if (!op.Value.m_hasValue)
return true;
return op.Value.m_value != value;
}
template <typename T2>
bool operator!=(const T2 &value, const Nullable<T2> &op)
{
if (!op.Value.m_hasValue)
return false;
return op.Value.m_value != value;
}
template <typename T2>
bool operator!=(const Nullable<T2> &op, std::nullptr_t nullpointer)
{
(void)nullpointer;
return op.Value.m_hasValue;
}
template <typename T2>
bool operator!=(std::nullptr_t nullpointer, const Nullable<T2> &op)
{
(void)nullpointer;
return op.Value.m_hasValue;
}
template <typename T>
const Nullable<T> & Nullable<T>::operator=(const Nullable<T> &value)
{
Value.m_hasValue = value.Value.m_hasValue;
Value.m_value = value.Value.m_value;
return *this;
}
template <typename T>
const Nullable<T> & Nullable<T>::operator=(const T &value)
{
Value.m_hasValue = true;
Value.m_value = value;
return *this;
}
template <typename T>
const Nullable<T> & Nullable<T>::operator=(std::nullptr_t nullpointer)
{
(void)nullpointer;
Value.m_hasValue = false;
Value.m_value = T();
return *this;
}
template <typename T>
T * Nullable<T>::operator->()
{
return &Value.m_value;
}
template <typename T>
const T * Nullable<T>::operator->() const
{
return &Value.m_value;
}
template <typename T>
T & Nullable<T>::operator*()
{
return Value.m_value;
}
template <typename T>
const T & Nullable<T>::operator*() const
{
return Value.m_value;
}
I tested it in gcc, clang and VS15 with the following:我在 gcc、clang 和 VS15 中对其进行了以下测试:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
(void)argc;
(void)argv;
Nullable<int> ni1;
Nullable<int> ni2 = nullptr;
Nullable<int> ni3 = 3;
Nullable<int> ni4 = 4;
ni4 = nullptr;
Nullable<int> ni5 = 5;
Nullable<int> ni6;
ni6 = ni3;
Nullable<int> ni7(ni3);
//Nullable<int> ni8 = NULL; // This is an error in gcc/clang but it's ok in VS12
cout << (ni1 == nullptr ? "True" : "False") << endl; // True
cout << (ni2 == nullptr ? "True" : "False") << endl; // True
cout << (ni2 == 3 ? "True" : "False") << endl; // False
cout << (ni2 == ni3 ? "True" : "False") << endl; // False
cout << (ni3 == 3 ? "True" : "False") << endl; // True
cout << (ni2 == ni4 ? "True" : "False") << endl; // True
cout << (ni3 == ni5 ? "True" : "False") << endl; // False
cout << (ni3 == ni6 ? "True" : "False") << endl; // True
cout << (ni3 == ni7 ? "True" : "False") << endl; // True
//cout << ni1 << endl; // Doesn't compile
//cout << ni3 << endl; // Doesn't compile
cout << ni3.Value << endl; // 3
//cout << ni1.Value << endl; // Throw exception
//cout << ni2.Value << endl; // Throw exception
//ni3.Value = 2; // Doesn't compile
cout << sizeof(ni1) << endl; // 8 on VS15
return 0;
}
There are lot of Nullable type implementation for C++ and most are incomplete. C++ 有很多 Nullable 类型的实现,而且大多数都不完整。 In C++ world, nullable types are called optional types .
在 C++ 世界中,可为空的类型称为可选类型。 This was proposed for C++14 but got postponed.
这是为 C++14 提出的,但被推迟了。 However the code to implement it compiles and works on most C++11 compilers.
然而,实现它的代码可以在大多数 C++11 编译器上编译和工作。 You can just drop in the single header file implementing optional type and start using it:
您可以只放入实现可选类型的单个头文件并开始使用它:
https://raw.githubusercontent.com/akrzemi1/Optional/master/optional.hpp https://raw.githubusercontent.com/akrzemi1/Optional/master/optional.hpp
Sample usage:示例用法:
#if (defined __cplusplus) && (__cplusplus >= 201700L)
#include <optional>
#else
#include "optional.hpp"
#endif
#include <iostream>
#if (defined __cplusplus) && (__cplusplus >= 201700L)
using std::optional;
#else
using std::experimental::optional;
#endif
int main()
{
optional<int> o1, // empty
o2 = 1, // init from rvalue
o3 = o2; // copy-constructor
if (!o1) {
cout << "o1 has no value";
}
std::cout << *o2 << ' ' << *o3 << ' ' << *o4 << '\n';
}
More documentation: http://en.cppreference.com/w/cpp/experimental/optional更多文档: http ://en.cppreference.com/w/cpp/experimental/optional
Also see my other answer: https://stackoverflow.com/a/37624595/207661另请参阅我的其他答案: https ://stackoverflow.com/a/37624595/207661
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.