Why don't the std::fstream classes take a std::string?
Solution 1
By taking a C string the C++03 std::fstream
class reduced dependency on the std::string
class. In C++11, however, the std::fstream
class does allow passing a std::string
for its constructor parameter.
Now, you may wonder why isn't there a transparent conversion from a std:string
to a C string, so a class that expects a C string could still take a std::string
just like a class that expects a std::string
can take a C string.
The reason is that this would cause a conversion cycle, which in turn may lead to problems. For example, suppose std::string
would be convertible to a C string so that you could use std::string
s with fstream
s. Suppose also that C string are convertible to std::string
s as is the state in the current standard. Now, consider the following:
void f(std::string str1, std::string str2);
void f(char* cstr1, char* cstr2);
void g()
{
char* cstr = "abc";
std::string str = "def";
f(cstr, str); // ERROR: ambiguous
}
Because you can convert either way between a std::string
and a C string the call to f()
could resolve to either of the two f()
alternatives, and is thus ambiguous. The solution is to break the conversion cycle by making one conversion direction explicit, which is what the STL chose to do with c_str()
.
Solution 2
There are several places where the C++ standard committee did not really optimize the interaction between facilities in the standard library.
std::string
and its use in the library is one of these.
One other example is std::swap
. Many containers have a swap member function, but no overload of std::swap is supplied. The same goes for std::sort
.
I hope all these small things will be fixed in the upcoming standard.
Solution 3
Maybe it's a consolation: all fstream's have gotten an open(string const &, ...) next to the open(char const *, ...) in the working draft of the C++0x standard. (see e.g. 27.8.1.6 for the basic_ifstream declaration)
So when it gets finalised and implemented, it won't get you anymore :)
Solution 4
The stream IO library has been added to the standard C++ library before the STL. In order to not break backward compatibility, it has been decided to avoid modifying the IO library when the STL was added, even if that meant some issues like the one you raise.
Solution 5
@ Bernard:
Monoliths "Unstrung." "All for one, and one for all" may work for Musketeers, but it doesn't work nearly as well for class designers. Here's an example that is not altogether exemplary, and it illustrates just how badly you can go wrong when design turns into overdesign. The example is, unfortunately, taken from a standard library near you...
~ http://www.gotw.ca/gotw/084.htm
Comments
-
Tom Duckering over 4 years
This isn't a design question, really, though it may seem like it. (Well, okay, it's kind of a design question). What I'm wondering is why the C++
std::fstream
classes don't take astd::string
in their constructor or open methods. Everyone loves code examples so:#include <iostream> #include <fstream> #include <string> int main() { std::string filename = "testfile"; std::ifstream fin; fin.open(filename.c_str()); // Works just fine. fin.close(); //fin.open(filename); // Error: no such method. //fin.close(); }
This gets me all the time when working with files. Surely the C++ library would use
std::string
wherever possible? -
outis over 13 yearsThe sample shouldn't cause any ambiguity with the overload resolution rules.
f(char*,std::string)
is an exact match while the others would require conversion, hence the first would be the best viable function. If you removed the firstf
, then, as "(char*&,std::string&) -> (char*,std::string) -> (char*,char*)" is a better conversion sequence than "(char*&,std::string&) -> (char*,std::string) -> (std::string,char*)", the secondf
would be the best viable function. Am I missing something? -
Elazar Leibovich about 13 yearsThis micro-optimization is really idiotic, as (1) strings are tightly coupled to any construct of the language, especially with strings (this is similar to saying, I don't want
GMP
to depend on shorts), and (2)char*
is evil, you should not encourage users to use it, if they don't have to, and millisecond in compilation time is well worth it, finally (3) they could've defined<stringfwd>
and use string without depending on it, so I claim decoupling dependencies is not a valid reason at all for this case. -
Micha Wiedenmann over 11 yearsThe reason for an explicit cast using
c_str()
versus an implicit type conversion using a conversion constructor is, that a c string requires an additional\0
, therefor thestd::string
cannot return aconst char*
pointer to its internal representation and there would be a performance hit. Using an explicit cast, the programmer can decide much better.