Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to use ternary operator to conditionally assign `istream &`?

I have a constructor that accepts a std::istream & and checks it before assigning one of the members (a std::istream &). For example:

class Stream
{
    public:
    Stream(std::istream &is) : s_ {is.good() ? is : throw std::runtime_error {"Invalid input stream\n"}} 
        {}

    private:
    std::istream &s_;
};

The compiler complains that the constructor for std::basic_istream(const basic_istream &) is deleted (understandably, since you can't copy streams). But, I don't see where any copying is being done here? It must be within the ternary operator, because

Stream(std::istream &is) : s {is} {}

with no checking works fine. Where is the std::istream attempting to be copied? And how can I fix this?

like image 989
dav Avatar asked Jan 01 '23 18:01

dav


1 Answers

This GCC bug 64372.

From cppreference on the conditional operator, we learn that if one operands of the conditional operator is a throw-expression, "[t]he result of the conditional operator has the type and the value category of the other expression." Thus, your use of the conditional operator should result in a type of std::istream&, but GCC thinks it's std::istream.


To work around this GCC bug, use a helper function:

class Stream
{
    public:
    Stream(std::istream &is) : s_ {validated_stream(is)} 
        {}

    private:
    std::istream &s_;

    static std::istream &validated_stream(std::istream &is) {
        if (!is.good()) throw std::runtime_error {"Invalid input stream\n"};
        return is;
    }
};
like image 88
Justin Avatar answered Jan 14 '23 13:01

Justin