Skip to content

Commit 4999416

Browse files
Fix reference_wrapper ambiguity with format_as (#4434)
1 parent 55a8f6a commit 4999416

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

include/fmt/std.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,9 +678,28 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
678678
}
679679
};
680680

681+
namespace detail {
682+
template <typename T, typename Enable = void>
683+
struct has_format_as : std::false_type {};
684+
template <typename T>
685+
struct has_format_as<T, void_t<decltype(format_as(std::declval<const T&>()))>>
686+
: std::true_type {};
687+
688+
template <typename T, typename Enable = void>
689+
struct has_format_as_member : std::false_type {};
690+
template <typename T>
691+
struct has_format_as_member<
692+
T, void_t<decltype(formatter<T>::format_as(std::declval<const T&>()))>>
693+
: std::true_type {};
694+
} // namespace detail
695+
696+
// Guard against format_as because reference_wrappers are implicitly convertible
697+
// to T&
681698
template <typename T, typename Char>
682699
struct formatter<std::reference_wrapper<T>, Char,
683-
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
700+
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value &&
701+
!detail::has_format_as<T>::value &&
702+
!detail::has_format_as_member<T>::value>>
684703
: formatter<remove_cvref_t<T>, Char> {
685704
template <typename FormatContext>
686705
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const

test/std-test.cc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,36 @@ TEST(std_test, format_shared_ptr) {
414414
TEST(std_test, format_reference_wrapper) {
415415
int num = 35;
416416
EXPECT_EQ("35", fmt::to_string(std::cref(num)));
417+
EXPECT_EQ("35", fmt::to_string(std::ref(num)));
418+
EXPECT_EQ("35", fmt::format("{}", std::cref(num)));
419+
EXPECT_EQ("35", fmt::format("{}", std::ref(num)));
420+
}
421+
422+
// Regression test for https://github.com/fmtlib/fmt/issues/4424
423+
struct type_with_format_as {
424+
int x;
425+
};
426+
427+
int format_as(const type_with_format_as& t) { return t.x; }
428+
429+
TEST(std_test, format_reference_wrapper_with_format_as) {
430+
type_with_format_as t{20};
431+
EXPECT_EQ("20", fmt::to_string(std::cref(t)));
432+
EXPECT_EQ("20", fmt::to_string(std::ref(t)));
433+
EXPECT_EQ("20", fmt::format("{}", std::cref(t)));
434+
EXPECT_EQ("20", fmt::format("{}", std::ref(t)));
435+
}
436+
437+
struct type_with_format_as_string {
438+
std::string str;
439+
};
440+
441+
std::string format_as(const type_with_format_as_string& t) { return t.str; }
442+
443+
TEST(std_test, format_reference_wrapper_with_format_as_string) {
444+
type_with_format_as_string t{"foo"};
445+
EXPECT_EQ("foo", fmt::to_string(std::cref(t)));
446+
EXPECT_EQ("foo", fmt::to_string(std::ref(t)));
447+
EXPECT_EQ("foo", fmt::format("{}", std::cref(t)));
448+
EXPECT_EQ("foo", fmt::format("{}", std::ref(t)));
417449
}

0 commit comments

Comments
 (0)