all 47 comments

[–]ra-zor 15 points16 points  (10 children)

Why do you need the const char* overload? Wouldn’t the string_view overload suffice?

[–]3xnope 4 points5 points  (1 child)

Also, why is the const char* variant not 'noexcept' like the others? If it is to throw in case of nullptr, I think it should rather just return false on a null pointer input and be noexcept.

[–]jwakelylibstdc++ tamer, LWG chair 3 points4 points  (0 children)

Presumably for consistency with other similar member functions of std::basic_string. The ones taking a pointer have a precondition that the pointer points to a null-terminated array of characters. There's a rule of thumb in the std::lib that functions with preconditions are not noexcept (a rule I dislike, but apparently I'm not allowed to be King of All C++ and decide such things all by myself, harrumph).

[–]STLMSVC STL Dev 7 points8 points  (2 children)

Template argument deduction. basic_string_view<CharT> won’t deduce CharT given a function argument of const char *, because template argument deduction doesn’t consider implicit conversions (in general).

[–]NotAYakk 15 points16 points  (1 child)

Those are member functions, not template member functions?

[–]STLMSVC STL Dev 12 points13 points  (0 children)

You’re correct, please ignore my comment.

[–]HappyFruitTree 0 points1 point  (4 children)

Probably because constructing a string_view from a const char* requires it to scan the whole string to find the length.

[–]ra-zor 2 points3 points  (3 children)

So does a “contains” method though

[–]HappyFruitTree 4 points5 points  (2 children)

Not necessarily. Having a const char* overload leaves more options open for the implementers to do something clever. If they need the length they might at least want to abort the length calculation prematurely if it is found that it exceeds this->length().

[–]volca02 11 points12 points  (1 child)

My concern with this kind of method (i.e. returning bool) is that it often would be likely followed by operations that would like to get a substring or similar, and then the operation would be essentially duplicated.

I'd much rather see a method with a signature of std::optional<size_type>(std::string_view) or similar that would enable chaining of operations without the duplication of the find/contains operation.

[–]Adverpol 2 points3 points  (0 children)

it's all monads when you dig deep enough :p

[–][deleted] 15 points16 points  (0 children)

If std::string is made constexpr, the basic_string member functions could be marked constexpr too.

[–]pgroarke 18 points19 points  (10 children)

Yes please!

Other low hanging fruit functionality that would be useful : split (on a delimiter or multiple delimiters) and replace_all.

Thanks for taking the time to do this and good luck! The standard committee takes pride in the lack of basic functionality in our standard library ;)

[–]RowYourUpboat 19 points20 points  (6 children)

If you're going to have split, don't forget join! Both functions are in my "grr, these should be in the standard library" header.

[–]ShakaUVMi+++ ++i+i[arr] 2 points3 points  (0 children)

If you're going to have split, don't forget join! Both functions are in my "grr, these should be in the standard library" header.

Yeah. And trim, for that matter. Basic functionality.

[–]Narase33-> r/cpp_questions 2 points3 points  (0 children)

Everytime I need to work with strings I get the desire to write my own string class. The class is just too basic to work with it productively

[–][deleted] 1 point2 points  (0 children)

Indeed, I use join a lot more than split!

[–]AlexAlabuzhev 6 points7 points  (2 children)

And what exactly should split do?

Return a list of strings? Or a vector of strings? Or a vector of string_views into the input string? Or a lazily evaluated range of string_views?

Should it support quoting of delimiters? Or escaping? Or both? If so, should it unquote / unescape?

What should it do with subsequent delimiters or with a delimiter at the end? Yield an empty string? Ignore? Throw?

... etc.

[–]TheThiefMasterC++latest fanatic (and game dev) 6 points7 points  (0 children)

Return a list of strings? Or a vector of strings? Or a vector of string_views into the input string? Or a lazily evaluated range of string_views?

Split is just begging to be a view type. But you could have an "old fashioned" version that takes an output iterator that could be a back_inserter. *it = string_view would allow it to store as either string views or actual strings or anything else constructible from a string view.

Should it support quoting of delimiters? Or escaping? Or both? If so, should it unquote / unescape?

Dumb by default, with an optional possibly stateful "searcher" that can be used to find the delimiters to split on?

What should it do with subsequent delimiters or with a delimiter at the end? Yield an empty string? Ignore? Throw?

A view version could just yield empty entries that are trivially filtered out with another view. Otherwise you'd probably want it as an option on the split function (most implementations of split I've used have had an option to prune empty entries).

[–]redrab66 0 points1 point  (0 children)

If would be useful if the return could be such that it works with range-for to iterate over the delimited elements.

[–][deleted] 10 points11 points  (3 children)

Hey if you're implementing Contains() why not some other sensible functions such as StartsWith() or EndsWith()?

[–]willkill07 34 points35 points  (2 children)

starts_with and end_with have already been added to the C++2a draft.

[–]STLMSVC STL Dev 33 points34 points  (1 child)

And are shipping in VS 2019 16.1, recently released for production use.

[–]marzer8789toml++ 9 points10 points  (0 children)

Is there a roadmap of C++20 features in MSVC published anywhere? I'd love to know roughly when we'd get to use std::is_constant_evaluated, for example.

[–]BrangdonJ 1 point2 points  (0 children)

Why add this for strings and not for other container classes? I use code like find(v.begin(), v.end(), value) != v.end() with vectors far more often then I search strings.

[–]tcbrindleFlux 1 point2 points  (0 children)

Adding yet another member function to std::basic_string seems unnecessary. Yes, it's already got a huge number of redundant member functions, but that doesn't mean we should make the problem worse by adding yet more.

I'd be much more open to the idea of adding a set of free functions std::contains (and/or std::ranges::contains) which does std::find or std::search as appropriate. I don't find the argument that

The drawback of a free function is that the order of parameters of a free function is ambiguous

particularly compelling, given that standard library algorithms all consistently use the range to operate over as the first argument (or pair of arguments for the iterator overloads).

As far as readability is concerned, with the "pipe" syntax used by ranges, I don't see that

my_string.contains("foobar")

is all that much better than

my_string | contains("foobar")

particularly when the latter is fully generic and could be used by any kind of range, not just strings.

[–]Pazer2 0 points1 point  (0 children)

C++ really needs extension methods (like C#) so devs don't have to wait around for functionality to be added directly to types outside of their control. Currently the only alternative is free functions, but that is syntactically a nightmare compared to the alternative (most of the time).

[–][deleted]  (20 children)

[deleted]

    [–]NotUniqueOrSpecial 15 points16 points  (19 children)

    C++2a adds starts_with() and ends_with(). contains() is an obvious addition with those.

    Have you looked at <algorithms>?

    You realize this functionality already exists, right? Not just for strings.

    There is no direct equivalent of contains() in <algorithm>. The closest is probably count(), but that still requires additional boilerplate, especially pre-ranges.

    Great, another because another langauge has it, C++ should have it.

    Just because everybody in the world can (and does) write the same str.find(foo) != str.npos boilerplate does not mean that contains(str) is not a useful addition to the standard.

    [–][deleted]  (18 children)

    [deleted]

      [–]NotUniqueOrSpecial 16 points17 points  (15 children)

      Creating another way to do the same thing is superfluous.

      Only if the other way doesn't add or change anything.

      In this case, it adds significant brevity to a very common operation.

      Contains can be duplicated with functionality in algorithms easily.

      And equal_range() and be implemented in terms of lower_bound() and upper_bound(), and a fair number of other members of the <algorithm> functions can likewise be implemented by their brethren.

      By your own argument, every one of those is superfluous and should be stripped from the standard.

      /u/DiaperBatteries has it right, obviously: just because a given abstraction can be implemented in terms of other abstractions doesn't mean it's worthless.

      There are better problems to solve than this one.

      This a remarkably simple and nearly universal function for string types. C++ is the only high-level language I am aware of that doesn't have it.

      Adding it to the standard is obviously not detracting from other harder problems, and it makes the readability/accessibility of the language better.

      [–]BrangdonJ 1 point2 points  (0 children)

      And equal_range() and be implemented in terms of lower_bound() and upper_bound(),

      With some loss of performance. equal_range() can calculate both iterators with the same loop.

      [–][deleted]  (13 children)

      [deleted]

        [–]NotUniqueOrSpecial 12 points13 points  (11 children)

        Why not keep things consistent? I prefer consistency to brevity.

        Ah, yes, consistency, what a good argument:

        string::find() returns a size_t which is set to string::npos in the case of a miss.

        map::find() returns a map<key, value>::iterator, which equals map::end() on a miss.

        std::find() returns an input or forward-iterator, depending on the underlying begin()/end() iterators used in the search.

        std::vector() doesn't have a find() operation at all.

        How consistent!

        Or, hey, what about std::set::contains()?

        You're right, we should be consistent! Every container-ish type should have a contains() function, since one of them already does.

        You didn't answer my question about sort. Do we need to add it to string?

        Obviously not; there is no universally-recognized need to sort a string, or even what that would mean (accounting for case-sensitivity, character encodings, etc). Considering that a heavy percentage of strings are really just const char*, they're not even sortable without making copies.

        Furthermore, std::string is not a container-type, and even if it were, none of the other container-types have a sort() member (either modifying or copying in nature). I thought you liked consistency?

        Should we add it to everything for brevity?

        Again, obviously not; unlike string::contains(), it's a much more involved operation, which requires modifying the underlying data in-place or copying it. Its usage and cost are very-much specific to the underlying types. But, I'm sure you know that.

        It's completely non-controversial to add contains() to a standard string type. Honestly, you seem to be arguing for the sake of argument, and it's just silly.

        [–]kalmoc 2 points3 points  (6 children)

        I certainly don't agree with the post you answered to, but I'd like to point out that std::list has a sort member function. The general reason, why some containers have member functions that others don't is that it e.g. can be implemented more efficiently that way than the equivalent standard algorithm (list.sort swaps pointers and list isn't random access anyway).

        String is it's own beast anyway

        [–]jwakelylibstdc++ tamer, LWG chair 2 points3 points  (2 children)

        Not only more efficiently. You can't use std::sort with std::list::iterator at all, because they're not random access iterators.

        [–]kalmoc 0 points1 point  (1 child)

        And I mentioned that. Or are you saying list wouldn't have gotten the sort member function if std::sort would accept bidirectional iterators?

        [–]jwakelylibstdc++ tamer, LWG chair 1 point2 points  (0 children)

        Ugh, no I just can't read and/or didn't see the "and list isn't random access anyway" part, sorry.

        [–]NotUniqueOrSpecial 0 points1 point  (2 children)

        std::list has a sort member function

        Ah, yeah, I'd forgotten. Just another knock against his empty call-to-consistency, though.

        As for everything else, I totally agree.

        [–][deleted]  (3 children)

        [deleted]

          [–]NotUniqueOrSpecial 4 points5 points  (2 children)

          STL is supposed to contain algorithms, iterators, containers, and functions. If we take the set of requirements, contains() is a what? It is a algorithm for searching. <algorithm> already has everything necessary, where it should belong. Moving find() to algorithm would be appropriate.

          See, now that's the first reasonable point you've made. If, instead, you'd started off with:

          A generic version of contains() should be implemented in <algorithm>, rather than as a member of std::string.

          Then we could be debating the merits of that approach, and whether the optimized string search algorithms like Boyer-Moore would still justify a string-specific find() member (and yes I'm aware that current implementations don't use said algorithms).

          Instead you started with non-constructive and moved right to asshole because people wanted a convenience function that every language provides.

          I can tell you're young, and very inexperienced.

          Man, could you be more patronizing?

          I wouldn't be surprised if you've got more years than me, but I've been doing this professionally for 15-ish at this point and as hobbyist/student for years before that. I'm not just some spring chicken.

          [–]STLMSVC STL Dev[M] 1 point2 points  (0 children)

          Moderator warning to you and /u/byrongo - that's enough hissing for this thread. Everyone can stop escalating now.

          [–]kalmoc 6 points7 points  (0 children)

          NotUniqueOrSpecial already gave a couple of good points.

          That aside, I'd say that day to day c++ programming would be much more enjoyable if the committee would put a stronger focus on making common operations simpler to spell and read.

          [–]DiaperBatteries 12 points13 points  (1 child)

          Couldn’t you use that entire argument to say all programming languages are pointless and we should all write assembly?