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

前端 未结 2 1524
星月不相逢
星月不相逢 2021-02-19 13:57

There is a conversion function operator void*() const in C++ stream classes. so that all stream objects can be implicitly converted to void*<

2条回答
  •  闹比i
    闹比i (楼主)
    2021-02-19 14:27

    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 
    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 
    boost::optional 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?

提交回复
热议问题