19
votes

Is there a reason why in c++ std::string is not implicitly converted to bool? For example

std::string s = ""
if (s) { /* s in not empty */ }

as in other languages (e.g. python). I think it is tedious to use the empty method.

4
Because it has no operator bool(), because the standard doesn´t require one, because ... how should it be defined? Why is empty false and non-empty true in your opinion? - deviantfan
It just isn't (neither does an empty vector evaluate to false, etc.). Perhaps an oversight, but possibly purposefully omitted due to the already high number of implicit conversion bugs that arise. - Cameron
@deviantfan: it is not my opinion: it is common in many languages - Ruggero Turra
@RuggeroTurra But C++ isn´t like Python, JS & co. If you make conditions based on strings in asm (jumps etc.), the best you get is true everytime because the address is not 0. That´s where C and the C++ originated from, not from dynamic typing etc.etc. - deviantfan
It may be a common feature in many languages, but there certainly isn't a common convention among those languages for which string values should be considered false, and which should be considered true. - Benjamin Lindley

4 Answers

28
votes

This probably could be added now that C++11 has added the concepts of explicit conversions and contextual conversion.

When std::string was designed, neither of these was present though. That made classes that supported conversion to bool fairly difficult to keep safe. In particular, that conversion could (and would) happen in lots of cases you almost never wanted it to. For example, if we assume std::string converts to false if empty and otherwise to true, then you could use a string essentially anywhere an integer or pointer was intended.

Rather than telling you about the type mismatch, the compiler would convert the string to bool, and then the bool to an integer (false -> 0, true -> 1).

Things like this happened often enough with many early attempts at string types (and there were many) that the committee apparently decided it was better to keep implicit conversions to an absolute minimum (so about the only implicit conversion supported by string is to create a string object from a C-style string).

There were a number of methods devised for handling conversion to bool more safely. One was converting to void * instead, which prevented some problems, but not others (this was used by iostreams). There was also a "safe bool" idiom (actually, more like a "safe bool" theme, of which there were several variations). While these certainly improved control over what conversions would and wouldn't be allowed, most of them involved a fair amount of overhead (a typical safe bool required a base class of ~50 lines of code, plus derivation from that base class, etc.)

As to how explicit conversion and contextual conversion would help, the basic idea is pretty simple. You can (starting with C++11) mark a conversion function as explicit, which allows it to be used only where an explicit cast to the target type is used:

struct X {
    explicit operator bool() { return true; }
};

int main() { 
    X x;
    bool b1 = static_cast<bool>(x); // compiles
    bool b2 = x;   // won't compile
}

Contextual conversion adds a little to let the conversion to bool happen implicitly, but only in something like an if statement, so using a class with the conversion function above, you'd get:

X x;
if (x) // allowed

int y = x; // would require explicit cast to compile

I'd add that complaints about "orthogonality" seem quite inapplicable here. Although convenient, converting a string to a Boolean doesn't really make a lot of sense. If anything, we should complain about how strange it is for string("0") to convert to 1 (in languages where that happens).

11
votes

This article mentions some reasons why operator bool() can lead to surprising results.

Note that std::string is just a typedef for std::basic_string<char>. There is also std::wstring for multi-byte characters. An implicit conversion would let you write:

std::string foo = "foo";
std::wstring bar = "bar";
if (foo == bar) {
  std::cout << "This will be printed, because both are true!\n";
}
5
votes

std::string still has to coexist with C-style strings.

A C-style string is by definition "a contiguous sequence of characters terminated by and including the first null character", and is generally accessed via a pointer to its first character. An expression such as "hello, world" is, in most contexts, implicitly converted to a pointer to the first character. Such a pointer may then be implicitly converted to bool, yielding true if the pointer is non-null, false if it's null. (In C, it's not converted to bool, but it can still be used directly as a condition, so the effect is nearly the same.)

So, due to C++'s C heritage, if you write:

if ("") { ... }

the empty string is already treated as true, and that couldn't easily be changed without breaking C compatibility.

I suggest that having a C-style empty string evaluate as true and a C++ empty std::string evaluate as false would be too confusing.

And writing if (!s.empty()) isn't that difficult (and IMHO it's more legible).

3
votes

The closest thing to what you (and I) want, that I've been able to find, is the following.

You can define the ! operator on std::string's like so:

bool operator!(const std::string& s)
{
    return s.empty();
}

This allows you to do:

std::string s;
if (!s)             // if |s| is empty

And using a simple negation, you can do:

if (!!s)            // if |s| is not empty

That's a little awkward, but the question is, how badly do you want to avoid extra characters? !strcmp(...) is awkward, too, but we still functioned and got used to it, and many of us preferred it because it was faster than typing strcmp(...) == 0.

If anyone discovers a way to do if (s), in C++, please let us know.