Issue 1066 (#1080)

Implemented `as<T>()` and `is<T>()` with accompanying tests
diff --git a/include/json/value.h b/include/json/value.h
index d0af711..bf4f9c4 100644
--- a/include/json/value.h
+++ b/include/json/value.h
@@ -412,6 +412,10 @@
   bool isArray() const;
   bool isObject() const;
 
+  /// The `as<T>` and `is<T>` member function templates and specializations.
+  template <typename T> T as() const = delete;
+  template <typename T> bool is() const = delete;
+
   bool isConvertibleTo(ValueType other) const;
 
   /// Number of values in array or object
@@ -673,6 +677,41 @@
   ptrdiff_t limit_;
 };
 
+template <> inline bool Value::as<bool>() const { return asBool(); }
+template <> inline bool Value::is<bool>() const { return isBool(); }
+
+template <> inline Int Value::as<Int>() const { return asInt(); }
+template <> inline bool Value::is<Int>() const { return isInt(); }
+
+template <> inline UInt Value::as<UInt>() const { return asUInt(); }
+template <> inline bool Value::is<UInt>() const { return isUInt(); }
+
+#if defined(JSON_HAS_INT64)
+template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
+template <> inline bool Value::is<Int64>() const { return isInt64(); }
+
+template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
+template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
+#endif
+
+template <> inline double Value::as<double>() const { return asDouble(); }
+template <> inline bool Value::is<double>() const { return isDouble(); }
+
+template <> inline String Value::as<String>() const { return asString(); }
+template <> inline bool Value::is<String>() const { return isString(); }
+
+/// These `as` specializations are type conversions, and do not have a
+/// corresponding `is`.
+template <> inline float Value::as<float>() const { return asFloat(); }
+template <> inline const char* Value::as<const char*>() const {
+  return asCString();
+}
+#ifdef JSON_USE_CPPTL
+template <> inline CppTL::ConstString Value::as<CppTL::ConstString>() const {
+  return asConstString();
+}
+#endif
+
 /** \brief Experimental and untested: represents an element of the "path" to
  * access a node.
  */
diff --git a/src/test_lib_json/main.cpp b/src/test_lib_json/main.cpp
index fff5b2c..c0cc00e 100644
--- a/src/test_lib_json/main.cpp
+++ b/src/test_lib_json/main.cpp
@@ -3502,6 +3502,53 @@
   return runner.runCommandLine(argc, argv);
 }
 
+struct MemberTemplateAs : JsonTest::TestCase {
+  template <typename T, typename F>
+  JsonTest::TestResult& EqEval(T v, F f) const {
+    const Json::Value j = v;
+    return JSONTEST_ASSERT_EQUAL(j.as<T>(), f(j));
+  }
+};
+
+JSONTEST_FIXTURE_LOCAL(MemberTemplateAs, BehavesSameAsNamedAs) {
+  const Json::Value jstr = "hello world";
+  JSONTEST_ASSERT_STRING_EQUAL(jstr.as<const char*>(), jstr.asCString());
+  JSONTEST_ASSERT_STRING_EQUAL(jstr.as<Json::String>(), jstr.asString());
+#ifdef JSON_USE_CPPTL
+  JSONTEST_ASSERT_STRING_EQUAL(js.as<CppTL::ConstString>(), js.asConstString());
+#endif
+  EqEval(Json::Int(64), [](const Json::Value& j) { return j.asInt(); });
+  EqEval(Json::UInt(64), [](const Json::Value& j) { return j.asUInt(); });
+#if defined(JSON_HAS_INT64)
+  EqEval(Json::Int64(64), [](const Json::Value& j) { return j.asInt64(); });
+  EqEval(Json::UInt64(64), [](const Json::Value& j) { return j.asUInt64(); });
+#endif // if defined(JSON_HAS_INT64)
+  EqEval(Json::LargestInt(64),
+         [](const Json::Value& j) { return j.asLargestInt(); });
+  EqEval(Json::LargestUInt(64),
+         [](const Json::Value& j) { return j.asLargestUInt(); });
+
+  EqEval(69.69f, [](const Json::Value& j) { return j.asFloat(); });
+  EqEval(69.69, [](const Json::Value& j) { return j.asDouble(); });
+  EqEval(false, [](const Json::Value& j) { return j.asBool(); });
+  EqEval(true, [](const Json::Value& j) { return j.asBool(); });
+}
+
+class MemberTemplateIs : public JsonTest::TestCase {};
+
+JSONTEST_FIXTURE_LOCAL(MemberTemplateIs, BehavesSameAsNamedIs) {
+  const Json::Value values[] = {true, 142, 40.63, "hello world"};
+  for (const Json::Value& j : values) {
+    JSONTEST_ASSERT_EQUAL(j.is<bool>(), j.isBool());
+    JSONTEST_ASSERT_EQUAL(j.is<Json::Int>(), j.isInt());
+    JSONTEST_ASSERT_EQUAL(j.is<Json::Int64>(), j.isInt64());
+    JSONTEST_ASSERT_EQUAL(j.is<Json::UInt>(), j.isUInt());
+    JSONTEST_ASSERT_EQUAL(j.is<Json::UInt64>(), j.isUInt64());
+    JSONTEST_ASSERT_EQUAL(j.is<double>(), j.isDouble());
+    JSONTEST_ASSERT_EQUAL(j.is<Json::String>(), j.isString());
+  }
+}
+
 #if defined(__GNUC__)
 #pragma GCC diagnostic pop
 #endif