Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<string>, <sstream>: Is the _String_constructor_rvalue_allocator_tag internal constructor necessary? #4929

Open
frederick-vs-ja opened this issue Sep 3, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Sep 3, 2024

When considering the bug reported in LLVM-101960 for MSVC STL, I found that in addition to optional, one internal constructor of basic_string is buggy in a similar way (Godbolt link). The constructor was added by #919 in responding to review comments in #919 (comment).

Example

#include <string>
#include <string_view>

template<class>
constexpr bool is_basic_string_v = false;
template<class C, class T, class A>
constexpr bool is_basic_string_v<std::basic_string<C, T, A>> = true;

template<class>
constexpr bool is_basic_string_view_v = false;
template<class C, class T>
constexpr bool is_basic_string_view_v<std::basic_string_view<C, T>> = true;

struct NastyStringConverter {
    template<class T, std::enable_if_t<!is_basic_string_view_v<T> && !is_basic_string_v<T>, int> = 0>
    operator T() const { return T{}; }
};

int main()
{
    // in C++17 the initializer list ctor is selected
    // in C++20 the internal ctor is selected, which doesn't seem conforming
    std::string(NastyStringConverter{}, std::allocator<char>{}); 
}

STL/stl/inc/xstring

Lines 1148 to 1152 in 91e4255

basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al)
: _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) {
// Used exclusively by basic_stringbuf
_Construct_empty();
}

Instead of fixing this constructor, I guess it might be better to remove it. As of LWG-2593, move construction must not change the value of the source allocator, so it's arguable that there shouldn't be observable difference between move and copy construction. But it seems allowed that side effects can be different.


There's a similar thing in list:

STL/stl/inc/list

Lines 807 to 810 in 91e4255

template <class _Any_alloc>
explicit list(_Move_allocator_tag, _Any_alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) {
_Alloc_sentinel_and_proxy();
}

@frederick-vs-ja frederick-vs-ja added the question Further information is requested label Sep 3, 2024
@StephanTLavavej
Copy link
Member

We talked about this at the weekly maintainer meeting, reading the relevant Standardese WG21-N4988 [stringbuf.members]/10:

Returns: A basic_string<charT, traits, Allocator> object move constructed from the basic_stringbuf's underlying character sequence in buf. This can be achieved by first adjusting buf to have the same content as view().

Overall, we were mildly concerned about disrupting overload resolution for such a popular type with machinery not directly depicted in the Standard, even if it fixes highly pathological scenarios. We believe that virtually no code anywhere cares about whether an allocator is copy-constructed or move-constructed (even though move-constructing an allocator can theoretically transfer internal caching blah blah that doesn't participate in allocator equality). The basic_stringbuf wording just says that the basic_string is "move constructed" without directly talking about the allocator.

(However, it's fine to move-construct allocators when doing so doesn't require extra internal machinery to be added, e.g. as we're doing in _Node_handle's move constructor.)

To summarize: We are in favor of removing this tagged constructor and simply copy-constructing the allocator here. We believe that this will simplify the codebase and real world code won't ever care about the difference. If anyone complains about this in the future, we can ask LWG to clarify the handwavy wording here.

@StephanTLavavej StephanTLavavej added bug Something isn't working and removed question Further information is requested labels Sep 4, 2024
@frederick-vs-ja
Copy link
Contributor Author

frederick-vs-ja commented Sep 5, 2024

We believe that this will simplify the codebase and real world code won't ever care about the difference.

I've found that some allocators in libcxx's test suit intentionally behave differently between copy and move construction and such differences are being tested.
If we switch to copy-construct the allocator at this moment, perhaps the related tests still pass for MSVC STL, but I'm not sure whether the test will change in the future...

@StephanTLavavej
Copy link
Member

We talked about that at the weekly maintainer meeting and @CaseyCarter said he wasn't super motivated by the possibility of a library test suite adding wacky allocators in the future. I said "we can cross that bridge if we come to it, we have plenty of libcxx skips and can always add more". Casey then mentioned that if we were concerned about node allocators, they can potentially have free-lists that benefit from moving, but this is an allocator for strings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants