diff --git a/doc/examples/get_ref.cpp b/doc/examples/get_ref.cpp
new file mode 100644
index 00000000..a8a86853
--- /dev/null
+++ b/doc/examples/get_ref.cpp
@@ -0,0 +1,26 @@
+#include <json.hpp>
+
+using namespace nlohmann;
+
+int main()
+{
+    // create a JSON number
+    json value = 17;
+
+    // explicitly getting references
+    auto r1 = value.get_ref<const json::number_integer_t&>();
+    auto r2 = value.get_ref<json::number_integer_t&>();
+
+    // print the values
+    std::cout << r1 << ' ' << r2 << '\n';
+    
+    // incompatible type throws exception
+    try
+    {
+        auto r3 = value.get_ref<json::number_float_t&>();
+    }
+    catch(std::domain_error& ex)
+    {
+        std::cout << ex.what() << '\n';
+    }
+}
diff --git a/src/json.hpp b/src/json.hpp
index a91edba4..81c02857 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -2416,6 +2416,19 @@ class basic_json
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
+	/// helper function to implement get_ref without code duplication 
+	/// for const and non-const overloads
+	/// ThisType will be deduced as 'basic_jason' or 'const basic_json'
+	template<typename ReferenceType, typename ThisType>
+    static ReferenceType get_ref_impl(ThisType& obj)
+    {
+		using PointerType = typename std::add_pointer<ReferenceType>::type;
+        // delegate the call to get_ptr<>()
+        auto ptr = obj.template get_ptr<PointerType>();
+		if (ptr) return *ptr;
+		throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name());
+    }
+
   public:
 
     /// @name value access
@@ -2563,6 +2576,52 @@ class basic_json
         return get_impl_ptr(static_cast<const PointerType>(nullptr));
     }
 
+	/*!
+    @brief get a reference value (implicit)
+
+    Implict reference access to the internally stored JSON value. No copies are
+    made.
+
+    @warning Writing data to the referee of the result yields an undefined
+    state.
+
+    @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref
+    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref
+    number_float_t.
+
+    @return reference to the internally stored JSON value if the requested reference
+    type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise
+
+	@throw std::domain_error in case passed type @a ReferenceType is incompatible
+    with the stored JSON value
+
+    @complexity Constant.
+    */
+    template<typename ReferenceType, typename
+             std::enable_if<
+                 std::is_reference<ReferenceType>::value
+                 , int>::type = 0>
+    ReferenceType get_ref()
+    {
+		// delegate call to get_ref_impl
+		return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /*!
+    @brief get a reference value (implicit)
+    @copydoc get_ref()
+    */
+    template<typename ReferenceType, typename
+             std::enable_if<
+                 std::is_reference<ReferenceType>::value
+                 and std::is_const< typename std::remove_reference<ReferenceType>::type >::value
+                 , int>::type = 0>
+    ReferenceType get_ref() const
+    {
+		// delegate call to get_ref_impl
+		return get_ref_impl<ReferenceType>(*this);
+    }
+
     /*!
     @brief get a value (implicit)
 
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a733f097..0838b234 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -2416,6 +2416,19 @@ class basic_json
         return is_number_float() ? &m_value.number_float : nullptr;
     }
 
+	/// helper function to implement get_ref without code duplication 
+	/// for const and non-const overloads
+	/// ThisType will be deduced as 'basic_jason' or 'const basic_json'
+	template<typename ReferenceType, typename ThisType>
+    static ReferenceType get_ref_impl(ThisType& obj)
+    {
+		using PointerType = typename std::add_pointer<ReferenceType>::type;
+        // delegate the call to get_ptr<>()
+        auto ptr = obj.template get_ptr<PointerType>();
+		if (ptr) return *ptr;
+		throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " + obj.type_name());
+    }
+
   public:
 
     /// @name value access
@@ -2563,6 +2576,52 @@ class basic_json
         return get_impl_ptr(static_cast<const PointerType>(nullptr));
     }
 
+	/*!
+    @brief get a reference value (implicit)
+
+    Implict reference access to the internally stored JSON value. No copies are
+    made.
+
+    @warning Writing data to the referee of the result yields an undefined
+    state.
+
+    @tparam ReferenceType reference type; must be a reference to @ref array_t, @ref
+    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or @ref
+    number_float_t.
+
+    @return reference to the internally stored JSON value if the requested reference
+    type @a ReferenceType fits to the JSON value; throws std::domain_error otherwise
+
+	@throw std::domain_error in case passed type @a ReferenceType is incompatible
+    with the stored JSON value
+
+    @complexity Constant.
+    */
+    template<typename ReferenceType, typename
+             std::enable_if<
+                 std::is_reference<ReferenceType>::value
+                 , int>::type = 0>
+    ReferenceType get_ref()
+    {
+		// delegate call to get_ref_impl
+		return get_ref_impl<ReferenceType>(*this);
+    }
+
+    /*!
+    @brief get a reference value (implicit)
+    @copydoc get_ref()
+    */
+    template<typename ReferenceType, typename
+             std::enable_if<
+                 std::is_reference<ReferenceType>::value
+                 and std::is_const< typename std::remove_reference<ReferenceType>::type >::value
+                 , int>::type = 0>
+    ReferenceType get_ref() const
+    {
+		// delegate call to get_ref_impl
+		return get_ref_impl<ReferenceType>(*this);
+    }
+
     /*!
     @brief get a value (implicit)
 
diff --git a/test/unit.cpp b/test/unit.cpp
index ebf3fb2a..81b341f6 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -2604,6 +2604,22 @@ TEST_CASE("pointer access")
         CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
     }
 
+	SECTION("pointer access to const object_t")
+    {
+        using test_type = json::object_t;
+        const json value = {{"one", 1}, {"two", 2}};
+
+		// this should not compile
+		// test_type* p1 = value.get_ptr<test_type*>();
+
+        // check if pointers are returned correctly
+        const test_type* p2 = value.get_ptr<const test_type*>();
+        CHECK(*p2 == value.get<test_type>());
+
+        const test_type* const p3 = value.get_ptr<const test_type* const>();
+        CHECK(p2 == p3);
+    }
+
     SECTION("pointer access to array_t")
     {
         using test_type = json::array_t;
@@ -2740,6 +2756,176 @@ TEST_CASE("pointer access")
     }
 }
 
+TEST_CASE("reference access")
+{
+    // create a JSON value with different types
+    json json_types =
+    {
+        {"boolean", true},
+        {
+            "number", {
+                {"integer", 42},
+                {"floating-point", 17.23}
+            }
+        },
+        {"string", "Hello, world!"},
+        {"array", {1, 2, 3, 4, 5}},
+        {"null", nullptr}
+    };
+
+    SECTION("reference access to object_t")
+    {
+        using test_type = json::object_t;
+        json value = {{"one", 1}, {"two", 2}};
+
+        // check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+		CHECK_NOTHROW(value.get_ref<json::object_t&>());
+        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+    }
+
+	SECTION("const reference access to const object_t")
+    {
+        using test_type = json::object_t;
+        const json value = {{"one", 1}, {"two", 2}};
+
+		// this should not compile
+		// test_type& p1 = value.get_ref<test_type&>();
+
+        // check if references are returned correctly
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+    }
+
+    SECTION("reference access to array_t")
+    {
+        using test_type = json::array_t;
+        json value = {1, 2, 3, 4};
+
+        // check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_NOTHROW(value.get_ref<json::array_t&>());
+        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+    }
+
+    SECTION("reference access to string_t")
+    {
+        using test_type = json::string_t;
+        json value = "hello";
+
+        // check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_NOTHROW(value.get_ref<json::string_t&>());
+        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+    }
+
+    SECTION("reference access to boolean_t")
+    {
+        using test_type = json::boolean_t;
+        json value = false;
+
+        // check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+    }
+
+    SECTION("reference access to number_integer_t")
+    {
+        using test_type = json::number_integer_t;
+        json value = 23;
+
+		// check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+    }
+
+    SECTION("reference access to number_float_t")
+    {
+        using test_type = json::number_float_t;
+        json value = 42.23;
+
+		// check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+		CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
+    }
+}
+
 TEST_CASE("element access")
 {
     SECTION("array")