14
void assign(char_type& to, char_type from);

Why can't you just use the assignment operator instead of using this function? What is this used for?

user541686
  • 205,094
  • 128
  • 528
  • 886

3 Answers3

9

You actually use this function every time you use std::string :). std::string is actually a typedef for std::basic_string, which is defined as:

template< 
    class CharT, 
    class Traits = std::char_traits<CharT>, 
    class Allocator = std::allocator<CharT>
> class basic_string;

(see this). Pay particular attention to the Traits template parameter. If you were so inclined, the Traits template parameter allows you to customize certain attributes of the string class's behavior. One of these properties is what happens when you make an assignment.

Here is an example usage of this. It will force assignments to be lowercase.

#include <string>
#include <iostream>
#include <cctype>

struct ci_char_traits : public std::char_traits<char> {
    static void assign(char& r, const char& a)
    {
        r = std::tolower(a);
    }

    static char* assign(char* p, std::size_t count, char a)
    {
        for (std::size_t i = 0; i < count; ++i)
        {
            p[i] = std::tolower(a);
        }
    }
};

typedef std::basic_string<char, ci_char_traits> ci_string;

std::ostream& operator<<(std::ostream& os, const ci_string& str) {
    return os.write(str.data(), str.size());
}

int main()
{
    ci_string s1 = "Hello";

    // This will become a lower-case 'o'
    s1.push_back('O');

    // Will replace 'He' with lower-case 'a'
    s1.replace(s1.begin(), s1.begin()+2, 1, 'A');

    std::cout << s1 << std::endl;
}
nevsan
  • 1,385
  • 9
  • 8
  • 1
    It would be a much better answer if you could explain why that is useful, rather than merely finding a way to use it. :) – user541686 Aug 20 '12 at 03:00
  • Well, suppose that we wanted to have a string type `lc_string` that preserved the invariant that all characters are lower case. The first thing you would do, is probably override `char_traits::copy` (and `char_trains::move`, why not) so that when you initialize the string (`lc_string str = "HELLO"`), it would convert it to lower case. However, there is no way to preserve the 'lowercase' invariant beyond that unless you also override `char_traits::assign`, since the user of the string can always use `replace` or `push_back()`. – nevsan Aug 21 '12 at 15:10
  • 1
    How would that help in the presence of `operator[]` though? – Dan M. Apr 01 '18 at 22:41
3

This is because character traits are a way to produce variants of standard classes (like strings) and a primitive type's operator may not actually be what you want.

For instance, consider a class that stores case-insensitive strings; you might implement assign() in a way that stores the same thing for both a capital letter and its lowercase version. (For that matter, other character-trait operations such as equality would have to be overridden too.)

Kevin Grant
  • 5,363
  • 1
  • 21
  • 24
  • 1
    Interesting... but isn't that what `compare` is for? – user541686 Aug 19 '12 at 05:10
  • 2
    I suppose overriding `compare()` would be a better way to handle case-insensitivity; there are probably better examples. My only point is that a subclass of character traits can do whatever it wants to, and it's certainly feasible that a class may want to do "more" than simply copy a character's exact value (as an operator would). – Kevin Grant Aug 19 '12 at 05:20
0

You can write your own character class, and define its operator=. But, you may find it convenient to use fundamental character types such as char, wchar_t, char16_t, char32_t or char8_t. Such non-class types don't allow you to overload their operators, and so char_traits provides a way to customise a small set of their common operations (as suggested in other answers, this could allow case-insensitive character operations, for example).

template < 
  class CharT, 
  class Traits = std::char_traits<CharT>, 
  class Allocator = std::allocator<CharT>
> class basic_string;

std::basic_string is a good example; the second template parameter, Traits, allows access to such customisation. Consider also the third template parameter, Allocator, which allows the user of std::basic_string to customise how it allocates memory internally. This could lead to a similar question: why not just use operator new?

Less significant, but note too that C++20 has introduced a second overload of std::char_traits<CharT>::assign:

static constexpr char_type* assign(char_type* p, std::size_t count, char_type a);
user2023370
  • 10,488
  • 6
  • 50
  • 83