2268. Setting a default argument in the declaration of a member function assign of std::basic_string

Section: 27.4.3 [basic.string] Status: C++14 Submitter: Vladimir Grigoriev Opened: 2013-06-26 Last modified: 2016-11-12

Priority: Not Prioritized

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with C++14 status.

Discussion:

Constructors and member functions assign of class std::basic_string have one to one relation (except the explicit constructor that creates an empty string). The following list shows this relation:

explicit basic_string(const Allocator& a = Allocator());

basic_string(const basic_string& str);
basic_string& assign(const basic_string& str);

basic_string(basic_string&& str) noexcept;
basic_string& assign(basic_string&& str) noexcept;

basic_string(const basic_string& str, size_type pos, size_type n = npos,
  const Allocator& a = Allocator());
basic_string& assign(const basic_string& str, size_type pos,
  size_type n);

basic_string(const charT* s,
  size_type n, const Allocator& a = Allocator());
basic_string& assign(const charT* s, size_type n);

basic_string(const charT* s, const Allocator& a = Allocator());
basic_string& assign(const charT* s);

basic_string(size_type n, charT c, const Allocator& a = Allocator());
basic_string& assign(size_type n, charT c);

template<class InputIterator>
basic_string(InputIterator begin, InputIterator end,
  const Allocator& a = Allocator());
template<class InputIterator>
basic_string& assign(InputIterator first, InputIterator last);

basic_string(initializer_list<charT>, const Allocator& = Allocator());
basic_string& assign(initializer_list<charT>);

So in fact any creating of an object of type std::basic_string using any of the above constructors except the explicit constructor can be substituted for creating a (possibly non-empty) string and then applying to it the corresponding method assign.

For example these two code snippets give the same result:

std::string s("Hello World");

and

std::string s;
s.assign("Hello World");

However there is one exception that has no a logical support. It is the pair of the following constructor and member function assign

basic_string(const basic_string& str, size_type pos, size_type n = npos,
             const Allocator& a = Allocator());

basic_string& assign(const basic_string& str, size_type pos, size_type n);

The third parameter of the constructor has a default argument while in the assign function it is absent. So it is impossible one to one to substitute the following code snippet

std::string s("Hello World");
std::string t(s, 6);

by

std::string s("Hello World");
std::string t;
t.assign(s, 6); // error: no such function

To get an equivalent result using the assign function the programmer has to complicate the code that is error-prone

std::string s("Hello World");
std::string t;
t.assign(s, 6, s.size() - 6); 

To fix that, the declaration of the member function assign should be changed in such a way that its declaration would be fully compatible with the declaration of the corresponding constructor, that is to specify the same default argument for the third parameter of the assign.

The assign function is not the only function that requires to be revised.

Now let include in the list of pairs constructor-assign with the modified method assign one more member function append. We will get:

explicit basic_string(const Allocator& a = Allocator());

basic_string(const basic_string& str);
basic_string& assign(const basic_string& str);
basic_string& append(const basic_string& str);

basic_string(basic_string&& str) noexcept;
basic_string& assign(basic_string&& str) noexcept;

basic_string(const basic_string& str, size_type pos, size_type n = npos,
  const Allocator& a = Allocator());
basic_string& assign(const basic_string& str, size_type pos,
  size_type n);
basic_string& append(const basic_string& str, size_type pos,
  size_type n);

basic_string(const charT* s,
  size_type n, const Allocator& a = Allocator());
basic_string& assign(const charT* s, size_type n);
basic_string& append(const charT* s, size_type n);

basic_string(const charT* s, const Allocator& a = Allocator());
basic_string& assign(const charT* s);
basic_string& append(const charT* s);

basic_string(size_type n, charT c, const Allocator& a = Allocator());
basic_string& assign(size_type n, charT c);
basic_string& append(size_type n, charT c);

template<class InputIterator>
basic_string(InputIterator begin, InputIterator end,
  const Allocator& a = Allocator());
template<class InputIterator>
basic_string& assign(InputIterator first, InputIterator last);
template<class InputIterator>
basic_string& append(InputIterator first, InputIterator last);

basic_string(initializer_list<charT>, const Allocator& = Allocator());
basic_string& assign(initializer_list<charT>);
basic_string& append(initializer_list<charT>);

As it seen from this record:

basic_string(const basic_string& str, size_type pos, size_type n = npos,
  const Allocator& a = Allocator());
basic_string& assign(const basic_string& str, size_type pos,
  size_type n);
basic_string& append(const basic_string& str, size_type pos,
  size_type n);

it is obvious that the function append also should have the default argument that is that it should be declared as:

basic_string& append(const basic_string& str, size_type pos,
  size_type n = npos);

In fact there is no a great difference in using assign or append especially when the string is empty:

std::string s("Hello World");
std::string t;
t.assign(s, 6);
 
std::string s("Hello World");
std::string t;
t.append(s, 6);

In both cases the result will be the same. So the assign and append will be interchangeable from the point of view of used arguments.

There are another three member functions in class std::basic_string that could be brought in conformity with considered above functions. They are member functions insert, replace, and compare.

So it is suggested to substitute the following declarations of insert, replace, and compare:

basic_string& insert(size_type pos1, const basic_string& str,
  size_type pos2, size_type n);

basic_string& replace(size_type pos1, size_type n1,
  const basic_string& str, size_type pos2, size_type n2);

int compare(size_type pos1, size_type n1,
  const basic_string& str, size_type pos2, size_type n2) const;

by the declarations:

basic_string& insert(size_type pos1, const basic_string& str,
  size_type pos2, size_type n = npos);

basic_string& replace(size_type pos1, size_type n1,
  const basic_string& str, size_type pos2, size_type n2 = npos);

int compare(size_type pos1, size_type n1,
  const basic_string& str, size_type pos2, size_type n2 = npos) const;

[2013-09 Chicago]

Howard: Are we positive this won't conflict with any other overloads?

They all appear to be unambiguous.

Alisdair: Ok, move to Ready.

Proposed resolution:

  1. Change class template basic_string synopsis, 27.4.3 [basic.string] p5, as indicated:

    namespace std {
      template<class charT, class traits = char_traits<charT>,
        class Allocator = allocator<charT> >
      class basic_string {
      public:
        […]
        basic_string& append(const basic_string& str, size_type pos, size_type n = npos);
        […]
        basic_string& assign(const basic_string& str, size_type pos, size_type n = npos);
        […]
        basic_string& insert(size_type pos1, const basic_string& str, size_type pos2, size_type n = npos);
        […]
        basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos);
        […]
        int compare(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos) const;
        […]
      };
    }
    
  2. Change 27.4.3.7.2 [string.append] before p3 as indicated:

    basic_string& append(const basic_string& str, size_type pos, size_type n = npos);
    
  3. Change 27.4.3.7.3 [string.assign] before p4 as indicated:

    basic_string& assign(const basic_string& str, size_type pos, size_type n = npos);
    
  4. Change 27.4.3.7.4 [string.insert] before p5 as indicated:

    basic_string& insert(size_type pos1, const basic_string& str, size_type pos2, size_type n = npos);
    
  5. Change 27.4.3.7.6 [string.replace] before p5 as indicated:

    basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos);
    
  6. Change 27.4.3.8.4 [string.compare] before p4 as indicated:

    int compare(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2 = npos) const;