Why operator void*() conversion function added to the C++ stream classes?
Asked Answered
B

2

14

There is a conversion function operator void*() const in C++ stream classes. so that all stream objects can be implicitly converted to void*. During the interaction with programmers on SO they suggest me to don't use void* unless you've a good reason to use it. void* is a technique of removing type safety & error checking. So, due to the existance of this conversion function following program is perfectly valid. This is a flaw in the C++ standard library.

#include <iostream>
int main()
{
       delete std::cout;
       delete std::cin;
}

See live demo here.

The above program is valid in C++03 but fails in compilation in C++11 & later compilers, because this conversion function is removed. But question is why it was part of C++ standard library if it is dangerous? What was the purpose of allowing conversion of stream objects to void*? What is the use of it?

Brockbrocken answered 23/8, 2015 at 16:38 Comment(3)
fwiw I think these programs are not "valid", they just happen to compile. but they surely give undefined behavior.Paschasia
This might interest you: artima.com/cppsource/safeboolP.htmlProvocation
@moooeeeep: yes, it is definitely interesting post. Thanks for the link,Brockbrocken
P
25

A feature of std::stringstream is that it is intended that if the stream is used as a bool, it gets converted to true if the stream is still valid and false if it's not. For instance this lets you use a simple syntax if you implement your own version of lexical cast.

(For reference, boost contains a template function called lexical_cast which does something similar to the following simple template function.)

template <typename T, typename U>
T lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        throw bad_lexical_cast();
    }
 }

If for instance, you work on a project where exceptions are disallowed, you might want to roll the following version of this instead.

template <typename T, typename U>
boost::optional<T> lexical_cast(const U & u) {
    T t;
    std::stringstream ss;
    if (ss << u && ss >> t) {
        return t;
    } else {
        return boost::none;
    }
 }

(There are various ways the above could be improved, this code is just to give an example.)

The operator void * is being used in the above as follows:

  1. ss << u returns a reference to ss.
  2. The ss is implicitly converted to void * which is nullptr if the stream operation failed, and non-null if the stream is still good.
  3. The && operator aborts quickly, depending on the truth value of that pointer.
  4. The second part ss >> t runs, and returns and is also converted to void *.
  5. If both operations succeeded then we can return the streamed result t. If either failed then we signal an error.

The bool conversion feature is basically just syntactic sugar that allows this (and many other things) to be written concisely.

The drawback of actually introducing an implicit bool conversion is that then, std::stringstream becomes implicitly convertible to int and many other types also, because bool is implicitly convertible to int. This ultimately causes syntactic nightmares elsewhere.

For instance, if std::stringstream had an implicit operator bool conversion, suppose you have this simple code:

std::stringstream ss;
int x = 5;
ss << x;

Now in overload resolution, you have two potential overloads to consider (!), the normal one operator<<(std::stringstream &, int), and the one in which ss is converted to bool, then promoted to int, and the bit shift operator may apply operator<<(int, int), all because of the implicit conversion to bool...

The workaround is to use an implicit conversion to void * instead, which can be used contextually as a bool, but isn't actually implicitly convertible to bool or int.

In C++11 we don't need this workaround any more, and there's no reason that anyone would have explicitly used the void * conversion, so it was just removed.

(I'm searching for a reference, but I believe that the value of this function was only specified up to when it should be nullptr vs. when it should not be nullptr, and that it was implementation defined what nonzero pointer values it might yield. So any code that relied on the void * version and cannot be trivially refactored to use the bool version would have been incorrect anyways.)

The void * workaround is still not without problems. In boost a more sophisticated "safe bool" idiom was developed for pre-C++11 code, which iiuc is based on something like T* where T is either a "type-used once", like a struct which is defined in the class implementing the idiom, or using a pointer-to-member function for that class and using return values which are either a particular private member function of that class, or nullptr. The "safe bool" idiom is used for instance in all of the boost smart pointers.

Regardless this whole mess was cleaned up in C++11 by introducing explicit operator bool.

More info here: Is the safe-bool idiom obsolete in C++11?

Paschasia answered 23/8, 2015 at 16:43 Comment(3)
Something is either deprecated or removed, not both. Deprecation means that usage is discouraged and removal will likely happen in the future.Unseemly
C++03 did not use the "safe bool" idiom. It instead used a conversion operator to void*. The safe bool idiom was a concept added to boost because while a conversion to bool was unsafe, so is the conversion to void*.Juncaceous
David Hammen: Thanks for pointing this out, will fix the answerPaschasia
J
12

This is a flaw in the C++ standard library.

This was a flaw in older versions (1998 and 2003) of the C++ standard library. This flaw no longer exists in 2011 and later versions of the standard. A new feature, the ability to mark conversion operators as explicit, was added to the language to make a conversion operator to bool safe.

The developers of the original version of the C++ standard explicitly chose to use a conversion to void* rather than a conversion to bool because a non-explicit conversion to bool was quite unsafe in a number of ways. In comparison, while operator void*() was rather obviously kludgy, it worked, at least so long as you didn't cast that pointer to something else or try to delete it. (An alternative, operator!, was arguably safer than either of those conversion operators, but that would have required the non-intuitive and abstruse while (!!stream) {...}. )

The concept of the "safe bool" idiom was developed after the original 1998/1999 version of the standard was released. Whether it was developed prior to 2003 is a bit irrelevant; the 2003 version of the standard was intended to be a bug fix to that original standard. That operator void*() let delete std::cin compile wasn't deemed a bug so much as a "don't do that then" kind of problem.

The development of the "safe bool" idiom showed that alternatives did exist that made operator bool() safe, but if you look at any of the implementations, they are all massively convoluted and massively kludgy. The C++11 solution was amazingly simple: Allow conversion operators to be qualified with the explicit keyword. The C++11 solution removed the void* conversion operator and added an explicit bool conversion operator. This made the "safe bool" idiom obsolete, at least so long as you are using a compiler that is C++11 (or later) compliant.

Juncaceous answered 23/8, 2015 at 18:35 Comment(1)
delete std::cin; – While it might have compiled, deleteing a void * pointer is undefined behavior anyway.Mayflower

© 2022 - 2024 — McMap. All rights reserved.