diff --git a/folly/lang/Exception.h b/folly/lang/Exception.h
index 9bc93c0bf96b0ed919b3f7be9658c670497d3658..8bb52cd59f43fdaa9f130896654b0adcf2b386bb 100644
--- a/folly/lang/Exception.h
+++ b/folly/lang/Exception.h
@@ -23,6 +23,7 @@
 #include <folly/CPortability.h>
 #include <folly/CppAttributes.h>
 #include <folly/Portability.h>
+#include <folly/Traits.h>
 
 namespace folly {
 
@@ -48,52 +49,75 @@ template <typename Ex>
   throw_exception(static_cast<Ex&&>(ex));
 }
 
-// clang-format off
 namespace detail {
-template <typename T>
-FOLLY_ERASE T&& to_exception_arg_(T&& t) {
-  return static_cast<T&&>(t);
-}
-template <std::size_t N>
-FOLLY_ERASE char const* to_exception_arg_(
-    char const (&array)[N]) {
-  return static_cast<char const*>(array);
-}
+
+struct throw_exception_arg_array_ {
+  template <typename R>
+  using v = std::remove_extent_t<std::remove_reference_t<R>>;
+  template <typename R>
+  using apply = std::enable_if_t<std::is_same<char const, v<R>>::value, v<R>*>;
+};
+struct throw_exception_arg_trivial_ {
+  template <typename R>
+  using apply = remove_cvref_t<R>;
+};
+struct throw_exception_arg_base_ {
+  template <typename R>
+  using apply = R;
+};
+template <typename R>
+using throw_exception_arg_ = //
+    conditional_t<
+        std::is_array<std::remove_reference_t<R>>::value,
+        throw_exception_arg_array_,
+        conditional_t<
+            is_trivially_copyable_v<remove_cvref_t<R>>,
+            throw_exception_arg_trivial_,
+            throw_exception_arg_base_>>;
+template <typename R>
+using throw_exception_arg_t =
+    typename throw_exception_arg_<R>::template apply<R>;
+
 template <typename Ex, typename... Args>
-[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args&&... args) {
-  throw_exception(Ex(static_cast<Args&&>(args)...));
+[[noreturn]] FOLLY_NOINLINE FOLLY_COLD void throw_exception_(Args... args) {
+  throw_exception(Ex(static_cast<Args>(args)...));
 }
 template <typename Ex, typename... Args>
 [[noreturn]] FOLLY_NOINLINE FOLLY_COLD void terminate_with_(
-    Args&&... args) noexcept {
-  throw_exception(Ex(static_cast<Args&&>(args)...));
+    Args... args) noexcept {
+  throw_exception(Ex(static_cast<Args>(args)...));
 }
+
 } // namespace detail
-// clang-format on
 
 /// throw_exception
 ///
 /// Construct and throw an exception if exceptions are enabled, or terminate if
 /// compiled with -fno-exceptions.
 ///
-/// Converts any arguments of type `char const[N]` to `char const*`.
+/// Does not perfectly forward all its arguments. Instead, in the interest of
+/// minimizing common-case inline code size, decays its arguments as follows:
+/// * refs to arrays of char const are decayed to char const*
+/// * refs to arrays are otherwise invalid
+/// * refs to trivial types are decayed to values
+///
+/// The reason for treating refs to arrays as invalid is to avoid having two
+/// behaviors for refs to arrays, one for the general case and one for where the
+/// inner type is char const. Having two behaviors can be surprising, so avoid.
 template <typename Ex, typename... Args>
 [[noreturn]] FOLLY_ERASE void throw_exception(Args&&... args) {
-  detail::throw_exception_<Ex>(
-      detail::to_exception_arg_(static_cast<Args&&>(args))...);
+  detail::throw_exception_<Ex, detail::throw_exception_arg_t<Args&&>...>(
+      static_cast<Args&&>(args)...);
 }
 
 /// terminate_with
 ///
-/// Terminates as if by forwarding to throw_exception but in a noexcept context.
-// clang-format off
+/// Terminates as if by forwarding to throw_exception within a noexcept context.
 template <typename Ex, typename... Args>
-[[noreturn]] FOLLY_ERASE void
-terminate_with(Args&&... args) noexcept {
-  detail::terminate_with_<Ex>(
-      detail::to_exception_arg_(static_cast<Args&&>(args))...);
+[[noreturn]] FOLLY_ERASE void terminate_with(Args&&... args) {
+  detail::terminate_with_<Ex, detail::throw_exception_arg_t<Args>...>(
+      static_cast<Args&&>(args)...);
 }
-// clang-format on
 
 /// invoke_cold
 ///