#include <iostream> // std::cout, std::boolalpha, std::endl
#include <string> // std::basic_string
#include <type_traits> // std::enable_if_t, std::is_same
#include <utility> // std::swap, std::move
namespace danger_zone // enter at your own peril
{
template<typename CharT>
struct nullable_basic_string : public std::basic_string<CharT>
{
protected:
using basic_string = std::basic_string<CharT>;
bool _isNull = false;
public:
using basic_string::basic_string; // inherit constructors
nullable_basic_string(basic_string const &str) : basic_string{ str }
{
}
nullable_basic_string(decltype(nullptr)) : basic_string{}, _isNull{ true }
{
}
nullable_basic_string() : nullable_basic_string{ nullptr }
{
}
nullable_basic_string& operator=(nullable_basic_string rhs)
{
using std::swap;
basic_string::swap(rhs);
swap(_isNull, rhs._isNull);
return *this;
}
friend bool operator==(nullable_basic_string const &lhs, decltype(nullptr) const &rhs)
{
return lhs._isNull;
}
friend bool operator==(decltype(nullptr) const &lhs, nullable_basic_string const &rhs)
{
return rhs == lhs;
}
friend bool operator!=(nullable_basic_string const &lhs, decltype(nullptr) const &rhs)
{
return !(lhs == rhs);
}
friend bool operator!=(decltype(nullptr) const &lhs, nullable_basic_string const &rhs)
{
return rhs != lhs;
}
};
using nullable_string = nullable_basic_string<char>;
////////////////////////////////////////////////////
// TODO: throw exception on null access //
////////////////////////////////////////////////////
} // namespace danger_zone
int main(int, char**) noexcept
{
using danger_zone::nullable_string;
nullable_string ns{};
nullable_string ns_copy{ ns };
nullable_string tmp_copy_n{ std::move(nullable_string{}) };
nullable_string tmp_copy_h{ std::move(nullable_string{ "Hello World!" }) };
nullable_string hello{ "Hello World!" };
nullable_string ns_assigned{};
ns_assigned = "Goodbye, Galaxy!";
nullable_string hello_assigned{ hello };
hello_assigned = nullptr;
std::cout << std::boolalpha << std::endl
<< "ns == nullptr? " << (ns == nullptr) << std::endl
<< "nullptr == ns? " << (nullptr == ns) << std::endl
<< "ns_copy == nullptr? " << (ns_copy == nullptr) << std::endl
<< "tmp_copy_n == nullptr? " << (tmp_copy_n == nullptr) << std::endl
<< "tmp_copy_h == nullptr? " << (tmp_copy_h == nullptr) << std::endl
<< "hello == nullptr? " << (hello == nullptr) << std::endl
<< "ns_assigned == nullptr? " << (ns_assigned == nullptr) << std::endl
<< "hello_assigned == nullptr? " << (hello_assigned == nullptr) << std::endl
<< ns_assigned << std::endl;
return 0;
}