5

I was learning the std::char_traits used in c++ string, then I realized that std::char_traits could be used to create customized string classes, like case_insensitive_string as in the cplusplus.com's article, I will paste the code sample below.

// char_traits::compare
#include <iostream>   // std::cout
#include <string>     // std::basic_string, std::char_traits
#include <cctype>     // std::tolower
#include <cstddef>    // std::size_t

// case-insensitive traits:
struct custom_traits: std::char_traits<char> {
    static bool eq (char c, char d) { return std::tolower(c)==std::tolower(d); }
    static bool lt (char c, char d) { return std::tolower(c)<std::tolower(d); }
    static int compare (const char* p, const char* q, std::size_t n) {
        while (n--) {if (!eq(*p,*q)) return lt(*p,*q); ++p; ++q;}
        return 0;
    }
};

int main ()
{
    std::basic_string<char,custom_traits> foo,bar;
    foo = "Test";
    bar = "test";
    if (foo==bar) std::cout << "foo and bar are equal\n";
    return 0;
}

As far as I know, static methods are part of the class, not instances. So we cannot override the static methods. The custom_trais class, however, explicitly inherits the char_trais class. Does anybody know how this works? Thanks.

hercule24
  • 85
  • 3
  • 7
  • 3
    Inheritance: yes. Runtime polymorphism: no. Welcome to the world of orthogonal language features. – Kerrek SB Feb 16 '16 at 17:47
  • 2
    Do remember that the type you pass as the traits type is statically known. The inheritance from `std::char_traits` only makes sure you don't have to retype all `typedefs` and other methods you don't want changed. – StoryTeller - Unslander Monica Feb 16 '16 at 17:49
  • OT: https://stackoverflow.com/questions/21805674/do-i-need-to-cast-to-unsigned-char-before-calling-toupper – Baum mit Augen Feb 16 '16 at 17:49
  • 2
    You might like this article, explaining the rationale behind these traits a bit: http://www.cantrip.org/traits.html – Daniel Jour Feb 16 '16 at 18:31

1 Answers1

4

OOP (intended as "classes with virtual functions") and generic programming are different programming paradigms which just happen to use the class-inheritance feature of C++ in two completely different ways to achieve their desired semantics.

In other words: Class inheritance in C++ per se is not tied to a particular programming paradigm.

In generic programming with type traits, OOP principles simply do not apply. You have nothing to override, and there is conceptually no such thing as a base type. Inheritance from a traits class is merely an effective way to make code easier to read and write. In theory, you don't necessarily have to do that, because you could instead just delegate to std::char_traits for all members you don't need to specialise:

struct custom_traits { // <------ no inheritance

    static bool eq (char c, char d) { return std::tolower(c)==std::tolower(d); }
    static bool lt (char c, char d) { return std::tolower(c)<std::tolower(d); }
    static int compare (const char* p, const char* q, std::size_t n) {
        while (n--) {if (!eq(*p,*q)) return lt(*p,*q); ++p; ++q;}
        return 0;
    }

    // delegate to std::char_traits:

    static void assign(char& r, const char& a) {
        std::char_traits<char>::assign(r, a);
    }

    static char* assign(char* p, std::size_t count, char a) {
        return std::char_traits<char>::assign(p, count, a);
    }

    // and so on
};

But what's the advantage of that? It's just more code to write and read.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62