Repository: microsoft/GSL (language: cpp)

Issue:
feat: narrow for non totally ordered types
The C++ Core Guidelines simply [specify `gsl::narrow`](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#gslutil-utilities) as `narrow // narrow<T>(x) is static_cast<T>(x) if static_cast<T>(x) == x or it throws narrowing_error`. The implementation uses relational operators, so I can't do `gsl::narrow<point<unsigned>>(point<int>{})` for my `point` type.


Discussion / hints:
Maintainers' call: Thanks for the feature suggestion. Because the GSL design and feature set is owned by the C++ Core Guidelines, this is just an implementation and we follow what the Guidelines specify. So we defer all feature suggestions by opening the issue in the Guidelines repo, and in the meantime we'll close the one here which can then be reopened later when there is guidance to follow from the Guidelines' making a design/feature change. Please reactivate this one when there's a conclusion.

Fix patch:
diff --git a/include/gsl/narrow b/include/gsl/narrow
index 67aac05e..40016d1e 100644
--- a/include/gsl/narrow
+++ b/include/gsl/narrow
@@ -26,7 +26,7 @@ struct narrowing_error : public std::exception
 };
 
 // narrow() : a checked version of narrow_cast() that throws if the cast changed the value
-template <class T, class U>
+template <class T, class U, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr>
 // clang-format off
 GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
 GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // TODO: MSVC /analyze does not recognise noexcept(false)
@@ -45,5 +45,22 @@ GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // TODO: MSVC /analyze does not recogn
 
     return t;
 }
+
+template <class T, class U, typename std::enable_if<!std::is_arithmetic<T>::value>::type* = nullptr>
+// clang-format off
+GSL_SUPPRESS(type.1) // NO-FORMAT: attribute
+GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // TODO: MSVC /analyze does not recognise noexcept(false)
+    // clang-format on
+    constexpr T narrow(U u) noexcept(false)
+{
+    const T t = narrow_cast<T>(u);
+
+    if (static_cast<U>(t) != u)
+    {
+        throw narrowing_error{};
+    }
+
+    return t;
+}
 } // namespace gsl
 #endif // GSL_NARROW_H


Test patch:
diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp
index 0585c792..39b4ca2f 100644
--- a/tests/utils_tests.cpp
+++ b/tests/utils_tests.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include <algorithm>   // for move
+#include <complex>
 #include <cstddef>     // for std::ptrdiff_t
 #include <functional>  // for reference_wrapper, _Bind_helper<>::type
 #include <gsl/narrow>  // for narrow, narrowing_error
@@ -144,5 +145,9 @@ TEST(utils_tests, narrow)
 
     n = -42;
     EXPECT_THROW(narrow<unsigned>(n), narrowing_error);
+
+    EXPECT_TRUE(
+        narrow<std::complex<float>>(std::complex<double>(4, 2)) == std::complex<float>(4, 2));
+    EXPECT_THROW(narrow<std::complex<float>>(std::complex<double>(4.2)), narrowing_error);
 }
 #endif // GSL_KERNEL_MODE