Don't let ADL pick to_string_view - #4938
Merged
Merged
Conversation
02bf4d1 disabled ADL for to_string_view by qualifying the is_string trait, and has_to_string_view and char_t are qualified for the same reason. Two call sites were left behind: value's string constructor and the write() overload for types with a string view conversion. Both sit inside fmt::detail, where an unqualified call adds the argument's own namespace to the overload set. That splits the two halves of one decision. Both call sites are reached only through has_to_string_view or char_t, which are defined by the qualified expression, so ADL can never be needed to satisfy them - it can only add candidates the gate never considered. When the argument's namespace declares a to_string_view template, the two tie during partial ordering and the call is ambiguous: core.h(2211): error C2668: 'to_string_view': ambiguous call to overloaded function note: could be 'string_view N::to_string_view<T>(const T&)' [found using argument-dependent lookup] note: or 'basic_string_view<char> fmt::detail::to_string_view<T,0>(const T&)' Both are reachable. format("{}", x) stores the argument through value's constructor; to_string(x) passes it to detail::write unmapped, which lands on the write() overload, as do FMT_COMPILE named fields and nested_formatter::write_arg. Reverting either line alone breaks the build of the test that covers it. This turned up in Microsoft Office, which declares a constrained to_string_view template next to its own string types. It only breaks where those types are distinct classes, so the same code compiles on platforms whose string types are std aliases - the ADL set is namespace std there and picks up nothing. One behaviour change worth noting: a non-template to_string_view in the argument's namespace used to win outright at these call sites, so a type that satisfies is_std_string_like via find_first_of and data() but has no size() formatted through ADL and now fails to compile, because the trait only checks that the qualified overload is viable, not that its body instantiates. That is the removed extension point going away rather than a new restriction; ADL to_string_view stopped being supported in 02bf4d1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
vitaut
requested changes
Sep 11, 2026
vitaut
left a comment
Contributor
There was a problem hiding this comment.
Adding namespace qualification seems OK but the tests are an overkill, let's drop them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
vitaut
approved these changes
Sep 12, 2026
Contributor
|
thank you |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
02bf4d1c("Disable to_string_view ADL") qualified theis_stringtrait, andhas_to_string_viewandchar_tare qualified for the same reason. Two call sites were missed:core.h—value's string constructorformat.h— thewrite()overload for types with a string view conversionBoth sit inside
fmt::detail, so an unqualified call adds the argument's own namespace to the overload set.That splits the two halves of one decision. Both call sites are reached only through
has_to_string_vieworchar_t, which are defined by the qualified expression — so ADL can never be needed to satisfy them, it can only add candidates the gate never considered. When the argument's namespace declares ato_string_viewtemplate, the two tie during partial ordering and the call is ambiguous:Both are reachable:
format("{}", x)stores the argument throughvalue's constructor.to_string(x)passes it todetail::writeunmapped, which lands on thewrite()overload — as doFMT_COMPILEnamed fields andnested_formatter::write_arg.This turned up in Microsoft Office, which declares a constrained
to_string_viewtemplate next to its own string types. It only breaks where those types are distinct classes; where they arestdaliases the ADL set is namespacestdand picks up nothing.Tests
base_test.adl_to_string_viewin core-test andformat_test.adl_to_string_viewin format-test. core-test cannot include format.h (it has an#errorguard), so each call site needs its own test. Reverting either line alone fails the build of the test that covers it.Full suite green at C++17 on MSVC 19.51 — 22/22 test binaries.
One behaviour change worth flagging
A non-template
to_string_viewin the argument's namespace previously won outright at these call sites. A type that satisfiesis_std_string_likethroughfind_first_ofanddata()but has nosize()therefore used to format through ADL, and now fails to compile: the trait only checks that the qualified overload is viable, not that its body instantiates. That is the extension point removed in 02bf4d1 going away rather than a new restriction.