diff --git a/include/fmt/base.h b/include/fmt/base.h index a862571b..abc6f2d2 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -1033,6 +1033,11 @@ enum { struct view {}; +template +struct is_view : std::false_type {}; +template +struct is_view> : std::is_base_of {}; + template struct named_arg; template struct is_named_arg : std::false_type {}; template struct is_static_named_arg : std::false_type {}; @@ -2726,7 +2731,7 @@ template struct fstring { template FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { using namespace detail; - static_assert(count<(std::is_base_of>::value && + static_assert(count<(is_view>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); diff --git a/test/format-test.cc b/test/format-test.cc index 77d299d1..d9e8bce2 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2540,3 +2540,29 @@ TEST(base_test, format_byte) { EXPECT_EQ(s, "42"); } #endif + +// Only defined after the test case. +struct incomplete_type; +extern const incomplete_type& external_instance; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + auto format(const incomplete_type& x, context& ctx) const -> appender; +}; +FMT_END_NAMESPACE + +TEST(incomplete_type_test, format) { + EXPECT_EQ(fmt::format("{}", external_instance), "42"); +} + +struct incomplete_type { + int i; +}; + +const incomplete_type& external_instance = {42}; + +auto fmt::formatter::format(const incomplete_type& x, + fmt::context& ctx) const + -> fmt::appender { + return formatter::format(x.i, ctx); +}