Istream Ctor

I'm having compiler errors associated with this block of text. I've added comments pertaining to the errors.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PwdReader //interface for reading the passwd file
{
...
    PwdReader(istream& i) //error: no appropriate default constructor for istream available
    {
        in = i; //error: operator= cannot access private member in class istream
    }
    virtual ~PwdReader()
    {

    }

private:
    istream in;
};
If for a moment we ignore std::istream as a whole, and focus on what you're doing, then maybe you'll get the point. In any class, all non-POD data members are usually best initialized in the class member initialization list i.e.
1
2
3
4
5
6
class Foo
{
    std::string f;
public:
    Foo( std::string const & f_ ): f( f_ ) {}
};


Did you see where I initialized the data member f? Doing Foo( std::string const & f_ ) { f = f_; } is called assignment, not initialization. Now you may ask What ( the f**k ) is the difference between the two? See here -> http://www.parashift.com/c++-faq/init-lists.html

IIRC, in C++, all subclasses of -- and including -- std::ios_base are not copyable, it means neither the class member initialization is allowed nor through assignment via the overloaded operator=. You have two options, however:

1) Make is a reference, allow your ctor take a std::istream & , let is reference the std::istream object.
2) Make your own std::istream using rdbuf


Below is 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

class PwdReader
{
    std::istream &is;
public:
    PwdReader( std::istream &s ): is( s ) {}
};

int main()
{
    PwdReader ref_reader{ std::cin };
    return 0;
}


Below is case 2:
1
2
3
4
5
6
7
8
9
10
11
12

#include <memory>
#include <iostream>

class PwdReader
{
    std::unique_ptr< std::istream > mem;
    std::istream & is;
public:
    PwdReader( std::istream & is_ ): mem( new std::istream( is_.rdbuf() ) ), is( *mem ) {}
    ~PwdReader() = default;
};
Standard streams are moveable (with a conforming implementation of the library).

1
2
3
4
5
6
    class PwdReader 
    {
        public: explicit PwdReader( std::ifstream&& i ) : in( std::move(i) ) {}
        // ...
        private: std::ifstream in ;
    };

Note: This wont work with the broken GNU library.
(Holding a reference and constructing with stream buffer will work everywhere.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <fstream>
#include <string>

class PwdReader_1 // reference
{
    public: explicit PwdReader_1( std::istream& i ) : in(i) 
            { std::string test ; std::getline(in,test) ; std::cout << test << '\n' ; }

    private: std::istream& in ;
};

#ifndef USING_BROKEN_GNU_LIBRARY
    class PwdReader_2 // move construct (make a copy via move)
    {
        public: explicit PwdReader_2( std::ifstream&& i ) : in( std::move(i) ) 
                { std::string test ; std::getline(in,test) ; std::cout << test << '\n' ; }
    
        private: std::ifstream in ;
    };
#endif // USING_BROKEN_GNU_LIBRARY

class PwdReader_3 // construct with streambuf
{
    public: explicit PwdReader_3( std::streambuf* buf ) : in(buf) 
            { std::string test ; std::getline(in,test) ; std::cout << test << '\n' ; }

    private: std::istream in ;
};

int main()
{
    {
        std::ifstream file( __FILE__ ) ;
        PwdReader_1 reader(file) ;
        // use reader
    }

    #ifndef USING_BROKEN_GNU_LIBRARY
    {
        PwdReader_2 reader( std::ifstream( __FILE__ ) ) ;
        // use reader
    }
    #endif // USING_BROKEN_GNU_LIBRARY

    {
        std::filebuf buf ;
        buf.open( __FILE__, std::ios::in ) ;
        PwdReader_3( std::addressof(buf) ) ;
        // use reader
    }
}

http://coliru.stacked-crooked.com/a/fe9ae014915d21b5
http://rextester.com/XINS63321
@JLBorges I'm aware of movable stream but what I didn't know is that the GNU library implementation is broken, I guess I should have used Clang when I was trying to compile the moving ctor.
On Linux, by default clang++ links with the GNU library; to link with LLVM libc++, specify -stdlib=libc++
(On BSDs, it links with libc++ by default; on Windows, clang++ links with the Microsoft library, which is fine.)
I've changed my code to PwdReader(istream& i): in(move(i)) {} and it's giving almost the same error, with the exception of now saying the method's trying to access a protected member.
The move constructor for std::istream is protected.
std::ifstream has a public move constructor.


> PwdReader(istream& i): in(move(i)) {}

This is a technical error: the constructor is called with an lvalue
To safely move the argument, it should be an rvalue.
In this case, std::ifstream&&

1
2
3
4
5
6
class PwdReader // move construct (make a copy via move)
{
    public: explicit PwdReader( std::ifstream&& i ) : in( std::move(i) ) {}
    
    private: std::ifstream in ;
};
Topic archived. No new replies allowed.