Home > Software engineering >  Need help understanding why this invalid C code compiles successfully
Need help understanding why this invalid C code compiles successfully

Time:09-21

The following code compile correctly for me in some environments (e.g. on compiler explorer with GCC 9.3.0) and complains in others (CentOS 7.9.2009 (Core), GCC 9.3.1).

#include <iostream>
#include <string>
using namespace std;

int main() {
    std::string name="aakash";
    name.erase(name.begin() 2, name.cend());
    return 0;
}

When I get an error, the error is:

test.cpp:6:40: error: no matching function for call to 'std::basic_string<char>::erase(__gnu_cxx::__normal_iterator<char*, std::basic_string<char> >, std::basic_string<char>::const_iterator)'
    6 |  name.erase(name.begin() 2, name.cend());
      |                                        ^
In file included from /opt/rh/devtoolset-9/root/usr/include/c  /9/string:55,
                 from /opt/rh/devtoolset-9/root/usr/include/c  /9/bits/locale_classes.h:40,
                 from /opt/rh/devtoolset-9/root/usr/include/c  /9/bits/ios_base.h:41,
                 from /opt/rh/devtoolset-9/root/usr/include/c  /9/ios:42,
                 from /opt/rh/devtoolset-9/root/usr/include/c  /9/ostream:38,
                 from /opt/rh/devtoolset-9/root/usr/include/c  /9/iostream:39,
                 from test.cpp:1:
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4698:7: note: candidate: 'std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::erase(std::basic_string<_CharT, _Traits, _Alloc>::size_type, std::basic_string<_CharT, _Traits, _Alloc>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::size_type = long unsigned int]'
 4698 |       erase(size_type __pos = 0, size_type __n = npos)
      |       ^~~~~
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4698:23: note:   no known conversion for argument 1 from '__gnu_cxx::__normal_iterator<char*, std::basic_string<char> >' to 'std::basic_string<char>::size_type' {aka 'long unsigned int'}
 4698 |       erase(size_type __pos = 0, size_type __n = npos)
      |             ~~~~~~~~~~^~~~~~~~~
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4714:7: note: candidate: 'std::basic_string<_CharT, _Traits, _Alloc>::iterator std::basic_string<_CharT, _Traits, _Alloc>::erase(std::basic_string<_CharT, _Traits, _Alloc>::iterator) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >; typename _Alloc::rebind<_CharT>::other::pointer = char*]'
 4714 |       erase(iterator __position)
      |       ^~~~~
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4714:7: note:   candidate expects 1 argument, 2 provided
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4734:7: note: candidate: 'std::basic_string<_CharT, _Traits, _Alloc>::iterator std::basic_string<_CharT, _Traits, _Alloc>::erase(std::basic_string<_CharT, _Traits, _Alloc>::iterator, std::basic_string<_CharT, _Traits, _Alloc>::iterator) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::basic_string<_CharT, _Traits, _Alloc>::iterator = __gnu_cxx::__normal_iterator<char*, std::basic_string<char> >; typename _Alloc::rebind<_CharT>::other::pointer = char*]'
 4734 |       erase(iterator __first, iterator __last);
      |       ^~~~~
/opt/rh/devtoolset-9/root/usr/include/c  /9/bits/basic_string.h:4734:40: note:   no known conversion for argument 2 from '__normal_iterator<const char*,[...]>' to '__normal_iterator<char*,[...]>'
 4734 |       erase(iterator __first, iterator __last);
      |                               ~~~~~~~~~^~~~~~
      |                               ~~~~~~~~~^~~~~~

The error looks reasonable to me because, as per C documentation, both arguments to std::basic_string::erase should either be of type iterator or const_iterator.

So, when it works (e.g. here), what allows it to work?

CodePudding user response:

Since C 11, both arguments to the two-iterator overload of std::string::erase() are const_iterator. See form #3 on this cppreference page (the one you linked in your question).

Further, the C 11 Standard requires that a conatiner's iterator types are convertible to the equivalent const_iterator.

From this Draft C 11 Standard, in §23.2.1 [container.requirements.general], Table 96 has the following entry (bold emphasis mine):

Expression Return Type Operational Semantics Assertion/note
X::iterator iterator type
whose value
type is T
any iterator category that
meets the forward iterator
requirements. convertible to
X::const_iterator.

So, in your call to .erase(), the first argument is being converted to a string::const_iterator.

Thus, the bug is in those compilers that don't accept your call, assuming you have set them also to use the C 11 (or later) standard.

  • Related