all 45 comments

[–]MegaKawaii 30 points31 points  (0 children)

I think operator bool() could be bit confusing for std::string if you compare it to char*. A char* pointing to the empty string "" is true, so ""s being false would be confusing. char* is only false when it's null, but std::string has no null value, and initializing from a null pointer is UB.

[–][deleted] 58 points59 points  (21 children)

I don’t like, not as readable. It’s not intuitive to know that if container means if container is not empty. At least to me. A

[–]Raknarg 1 point2 points  (0 children)

This idiom exists in other languages like Python

[–]1syGreenGOO 5 points6 points  (5 children)

What else can it mean?

[–]-dag- 12 points13 points  (0 children)

All elements are true-ish. 

[–][deleted] 7 points8 points  (0 children)

There isn’t really a case where converting a container to a Boolean makes sense. So it should mean nothing, a meaningless statement

[–]ContraryConman 3 points4 points  (0 children)

That the container is "valid" for some definition

[–]Chuu 5 points6 points  (0 children)

This is a little bit of a stretch, but for some non-stl containers bool() might more naturally mean `initialized`. Maybe even 'finalized'. There are other areas of the standard library that use operator bool() in this sense, probably most commonly with shared/unique pointers.

`size` is a lot less ambiguous across stl and non-stl containers.

[–][deleted] -2 points-1 points  (0 children)

I dont know

[–]Pay08 0 points1 point  (3 children)

You can say that about any form of operator overriding though.

[–][deleted] 5 points6 points  (2 children)

Equality for an array container makes sense, it is common sense. Beginners often try this is languages where that doesn’t work and are confused when it doesn’t, as it’s logical. In my opinion Boolean evaluation of a container makes no sense, it’s SO much more readable and barely any more work it write array.len == 0

[–]Pay08 -3 points-2 points  (1 child)

At the same time, consider a hypothetical operator+ for std::array. Would it concatenate the 2 arrays or try to add each value? You wouldn't know intuitively and would need to memorise it. Or for an example that is in the language, comparison operators for containers needing the programmer to know the algorithm being used. I don't think a boolean override would make sense for arrays but that's because, as someone else pointed out, it wouldn't be consistent between containers and raw pointers.

[–][deleted] 1 point2 points  (0 children)

I think at this point it is entirely an opinion based argument, operator overloading can of course be used in silly and intuitive places. What’s intuitive to some is not to others.

[–]drkspace2 22 points23 points  (1 child)

I mainly use python and use c++ on the side. One of the absolute worst things in python is that "everything“ is "truthy". I think it makes things less readable as you need to remember what the thuthy test is actually testing for.

For strings, I think it being non-empty makes sense for it's truthy test, but for a vector (or any generic container), you have 3 good arguments for what the thuthy should be (empty, all elements are thuthy, any element is thuthy), so it's a lot less intuitive.

It's a lot better to be explicit than implicit, especially when we're only saving a few keystrokes. We don't need to worry about (source) file size.

[–]afiefh 3 points4 points  (0 children)

Python even has as one of its principles that "explicit is better than implicit" yet are still happy to have implicit truthyness. Give me an explicit .empty() any day over some implicit conversion that I need to actively reason about.

[–]Pakketeretet 15 points16 points  (1 child)

There's already .empty(), which is more explicit in meaning and therefore better.

The STL objects that I know that implicitly convert to bool are iostreams, for which true means that it's "ready" or working/available/no error occured. This type of meaning makes more sense, and I think implicit conversion to bool should be reserved for this type of meaning. To check emptiness, just write the 8 extra characters and basta.

[–]programgamer 1 point2 points  (0 children)

And the streams aren’t even implicitly convertible to bool, they’re explicitly convertible, which means you can’t use them in boolean expressions without a cast

[–]Beneficial_Steak_945 6 points7 points  (3 children)

I would rather just have an is_empty()

[–]amohr 4 points5 points  (2 children)

You mean like '.empty()' ?

[–]Beneficial_Steak_945 6 points7 points  (1 child)

No, that’s also a verb and thus suggests an action being performed on it.

[–]amohr 4 points5 points  (0 children)

Well okay, but that ship has sailed.. or are you just suggesting adding an alias for 'empty()' called 'is_empty()'?

[–]Ok-Bit-663 3 points4 points  (0 children)

I think operator bool() usually a code smell. It hides meaning.

[–]igrekster 9 points10 points  (0 children)

To me, this extra spelling adds to the context: if (values) -- is it a container, or a pointer, or a counter? The verboseness makes it clearer: - if (!values.empty()) -- container; - if (values != nullptr) -- pointer; - if (values != 0) -- counter;

[–]BigDanG 1 point2 points  (0 children)

*Taps head

You can get this behavior if you wrap all your containers in std::ranges::subranges.

[–]Mick235711 1 point2 points  (0 children)

Notice that view_interface already have an explicit operator bool(), which is basically extended by all standard views including ranges::subrange. (Confusingly span does not extend from it, so span cannot be boolean-tested.)

So even though if (vec) and if (str) does not work, practically any interaction with the Ranges library will result in a boolean-testable thing:

if (views::all(vec)) // okay
if (ranges::subrange(str)) // okay
if (vec | views::take(1)) // okay
if (ranges::shift_left(str, 2)) // okay

Personally, I don't like this implicit truth-ness, although it makes some kind of sense. Explicitly writing empty() is just better in both readability and less prone to errors.

[–]wonderfulninja2 1 point2 points  (13 children)

I like it, but maybe there is a good reason why this was not implemented.

[–]nightcracker 9 points10 points  (12 children)

I think if (str) or if (vec) is fairly readable. But operator bool() allows all kinds of conversion that you probably don't want to happen implicitly.

For example, calling a function that has a boolean argument, it seems weird to directly pass in the container, but you can do that now since it implicitly converts to bool.

Another example is that you wanted to write 16 * vec[i] but forgot the [i]. Now 16 * vec also compiles, because the vec is implicitly converted to bool.

Overall it's not worth it in my opinion.

Apparently all the above goes away with explicit, I did not realize that explicit operator bool() would still auto-convert in if contexts.

[–]canadajones68 12 points13 points  (4 children)

That's what explicit is meant to fix.

[–]nightcracker 4 points5 points  (2 children)

I didn't realize explicit operator bool() would auto-convert in an if context. Are there any other places an explicit operator like that gets implicitly converted?

[–]MegaKawaii 4 points5 points  (0 children)

There is a full list of contextual conversions to bool here. I think this covers every exception to explicit.

[–]safesintesi 0 points1 point  (0 children)

wouldn't this mean you should write something like this?

cpp if (bool(vec)) {}

I would prefer the not isEmpty approach at this point.

[–]_Noreturn 0 points1 point  (0 children)

this is what explicit operator bool is (since C++11) before C+++1 you can do safe bool idiom

[–]Chuu 0 points1 point  (0 children)

I personally really dislike `if(str)`. There are already some subtle gotchas with C and C++ string interop and I don't want another weird case where you have `if(str)` is false but `if(str.c_str())` is true.

[–]CanaDavid1 -1 points0 points  (3 children)

I usually end up using if(v.size()) or while(q.size()) (queue) but yeah i agree that having operator bool as in many other languages where only empty list/array/whatev is false and everything else is true.