Home > OS >  Can I inherit from std::string to provide strongly typed string?
Can I inherit from std::string to provide strongly typed string?

Time:11-09

In a codebase I am working on, most functions takes as arguments several std::string. These strings represent different things, and I would like to define several classes to detect inconsistencies at compile time if possible.

Here is an example of problematic code:

void displayIdentity(const std::string &firstName, const std::string &lastName) {
    std::cout << "First name = " << firstName << "\t; " << "Last name = " << lastName << std::endl;
}

const std::string firstName = "John";
const std::string lastName  = "Doe";
displayIdentity(lastName, firstName); // clearly, it should have been 
                                      // displayIdentity(firstName, lastName)

The current solution I am using is to use different classes defined using template:

template <typename NAME> class TypedString : public std::string {
  public:
    explicit TypedString(const char* s) : std::string{s} {}
  
    explicit TypedString(const std::string& s) : std::string{s} {}
};

Then it becomes possible to detect inconsistency at compile time:

const FirstName firstName2{"John"};
const LastName  lastName2{"Doe"};
displayIdentity2(firstName2, lastName2);
// displayIdentity2(lastName2,  firstName2); // error: invalid initialization of reference of type 
                                             // 'const FirstName&' from expression of type 'LastName'
    
    
//const LastName lastName3{firstName2};      // error: invalid initialization, as expected

const LastName lastName3{std::string(firstName2)}; // but conversion can to requested explicitly 
    

I have started using this code, and it works as expected and helps me detect inconsistencies.

I have read on this post that we should not inherit from std::string because it's destructor is not virtual.

As the TypedString class is empty, it's destructors should be equivalent to the std::string destructor, so I don't see this as problem. Same things for methods, I don't care if std::string methods are called when using polymorphism, because I want TypedString to behave exactly like a string, except for construction/conversion...

Am I correct? Or should I use a more verbose solution (using composition as done here) to solve my problem?

CodePudding user response:

Please don't. Amongst others, std::string dtor is not virtual, type is not polymorphic; you'd only save some typing with that. If you do a generic template<typename T> class StrongType that works for any type and simply delegates everything unless specialized, then you only type it once and you can do all the specializations, etc.

CodePudding user response:

As long as you don't delete any instances of your TypedString via a pointer to std::string the lack of a virtual destructor is irrelevant.

That is, the behavior of the following is undefined since std::string's destructor is not virtual:

std::string* name = new FirstName{"John"};
delete name;

If you never use your class in that way you won't have any problems. It may be worth using private inheritance and explicitly exposing the member functions you need. That would prevent the snippet above from compiling at all, but it may also make explicit desired conversions more difficult or verbose.

  • Related