7

Why are those C++11 new functions of header <string> (stod, stof, stoull) not member functions of the string class ?

Isn't more C++ compliant to write mystring.stod(...) rather than stod(mystring,...)?

Greg82
  • 1,000
  • 3
  • 10
  • 24
  • 8
    They don't need to be, and `std::string` already has far too many member functions. See the [Monoliths "unstrung" GOTW](http://www.gotw.ca/gotw/084.htm). – juanchopanza Jan 29 '14 at 09:30
  • 2
    It would have been nice if `stod` was a template that could take any `Sequence` object instead of just `std::string` – Siler Jan 29 '14 at 09:30
  • 1
    std::string is already a [monolith of a class](http://www.gotw.ca/gotw/084.htm), it doesn't need more member functions. – The Forest And The Trees Jan 29 '14 at 09:32
  • There was a standards committee deciding this, so asking for "more C++ compliant" should ring a warning bell that the viewpoint here might be not the best – PlasmaHH Jan 29 '14 at 10:04

3 Answers3

22

It is a surprise to many, but C++ is not an Object-Oriented language (unlike Java or C#).

C++ is a multi-paradigm language, and therefore tries to use the best tool for the job whenever possible. In this instance, a free-function is the right tool.

Guideline: Prefer non-member non-friend functions to member functions (from Efficient C++, Item 23)

Reason: a member function or friend function has access to the class internals whereas a non-member non-friend function does not; therefore using a non-member non-friend function increases encapsulation.

Exception: when a member function or friend function provides a significant advantage (such as performance), then it is worth considering despite the extra coupling. For example even though std::find works really well, associative containers such as std::set provide a member-function std::set::find which works in O(log N) instead of O(N).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 3
    If that guideline would just have been applied a little sooner to `std::basic_string`... – pmr Jan 29 '14 at 10:36
  • @pmr: Yes, `std::basic_string` and all the IO Streams are dinosaurs that pre-existed standardization; actually, the original version of `string` was entirely defined in terms of indices, and during the standardization process all the iterator overloads were added to make a sequence of it... but the former methods were kept for compatibility. – Matthieu M. Jan 29 '14 at 12:11
  • What if the workings of the global non-member non-friend functions rely on private data members of the class? – David G Jan 29 '14 at 13:11
  • @0x499602D2: This is not possible by construction, since the very principle of a non-member non-friend function is to only have access to the `public` interface. – Matthieu M. Jan 29 '14 at 13:15
  • @MatthieuM. So this is implying that we should implement getters and setters? – David G Jan 29 '14 at 13:24
  • 4
    @0x499602D2: I am afraid I am not, at all, following your line of reasoning. I never said anything about getters and setters... If you want to implement a method that *absolutely* requires access to the class internals then *obviously* it will be implemented as either a member function OR a friend function; there is no debate here. The guideline applies *when you have the choice*, and *more often than not* you do have the choice, you just need to step back to realize it. When you don't, you don't. – Matthieu M. Jan 29 '14 at 13:30
3

The fundamental reason is that they don't belong there. They don't really have anything to do with strings. Stop and think about it. User defined types should follow the same rules as built-in types, so every time you defined a new user type, you'd have to add a function to std::string. This would actually be possible in C++: if std::string had a member function template to, without a generic implementation, you could add a specialization for each type, and call str.to<double>() or str.to<MyType>(). But is this really what you want. It doesn't seem like a clean solution to me, having everyone writing a new class having to add a specialization to std::string. Putting these sort of things in the string class bastardizes it, and is really the opposite of what OO tries to achieve.

If you were to insist on pure OO, they would have to be members of double, int, etc. (A constructor, really. This is what Python does, for example.) C++ doesn't insist on pure OO, and doesn't allow basic types like double and int to have members or special constructors. So free functions are both an acceptable solution, and the only clean solution possible in the context of the language.

FWIW: conversions to/from textual representation is always a delicate problem: if I do it in the target type, then I've introduced a dependency on the various sources and sinks of text in the target type---and these can vary in time. If I do it in the source or sink type, I make them dependent on the the type being converted, which is even worse. The C++ solution is to define a protocol (in std::streambuf), where the user writes a new free function (operator<< and operator>>) to handle the conversions, and counts on operator overload resolution to find the correct function. The advantage of the free function solution is that the conversions are part of neither the data type (which thus doesn't have to know of sources and sinks) nor the source or sink type (which thus doesn't have to know about user defined data types). It seems like the best solution to me. And functions like stod are just convenience functions, which make one particularly frequent use easier to write.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

Actually they are some utility functions and they don't need to be inside the main class. Similar utility functions such as atoi, atof are defined (but for char*) inside stdlib.h and they too are standalone functions.

Varo
  • 831
  • 1
  • 7
  • 16