This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++14 status.

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

Section: 21.3.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, 21.3.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 21.3.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 21.3.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 21.3.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 21.3.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 21.3.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;