Help needed for STL ifstream class

K

Kira Yamato

I've posted this in another thread, but I suppose I should've started a
new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),
// this line won't compile!
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

The compiler error messages are as followed:

/usr/include/c++/4.0.0/iosfwd: In copy constructor
'std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)':
/usr/include/c++/4.0.0/bits/ios_base.h:779: error:
'std::ios_base::ios_base(const std::ios_base&)' is private
/usr/include/c++/4.0.0/iosfwd:55: error: within this context
/usr/include/c++/4.0.0/iosfwd: In copy constructor
'std::basic_istream<char, std::char_traits<char> >::basic_istream(const
std::basic_istream<char, std::char_traits<char> >&)':
/usr/include/c++/4.0.0/iosfwd:61: warning: synthesized method
'std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)' first required here
a.cpp: In function 'int main(int, char**)':
a.cpp:10: warning: synthesized method 'std::basic_istream<char,
std::char_traits<char> >::basic_istream(const std::basic_istream<char,
std::char_traits<char> >&)' first required here
a.cpp:10: error: no matching function for call to
'std::istream_iterator<char, char, std::char_traits<char>,
ptrdiff_t>::istream_iterator(std::basic_istream<char,
std::char_traits<char> >)'
/usr/include/c++/4.0.0/bits/stream_iterator.h:70: note: candidates are:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(const std::istream_iterator<_Tp, _CharT,
_Traits, _Dist>&) [with _Tp = char, _CharT = char, _Traits =
std::char_traits<char>, _Dist = ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:66: note:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(std::basic_istream<_CharT, _Traits>&) [with
_Tp = char, _CharT = char, _Traits = std::char_traits<char>, _Dist =
ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:62: note:
std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator()
[with _Tp = char, _CharT = char, _Traits = std::char_traits<char>,
_Dist = ptrdiff_t]

Now, I have discovered that if I change the program into the following,
then it compiles fine:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
istream *ifile = argc >= 2 ? new ifstream(argv[1]) : &cin;
copy(istream_iterator<char>(*ifile),
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

I know this works, but it would be nice to understand why the original
version does not work anyway.

Thank you for your help.
 
V

Victor Bazarov

Kira said:
I've posted this in another thread, but I suppose I should've started
a new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),

'istream_iterator's constructor that accepts a stream object takes the
argument by non-const reference. A non-const reference cannot be bound
to a temporary. You need to create a separate object of type 'ifstream'
and then pass it to the 'istream_iterator's constructor.
// this line won't compile!
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

The compiler error messages are as followed:

[..]

V
 
B

Barry

Kira said:
I've posted this in another thread, but I suppose I should've started a
new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin), //
this line won't compile!
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

The compiler error messages are as followed:

/usr/include/c++/4.0.0/iosfwd: In copy constructor 'std::basic_ios<char,
std::char_traits<char> >::basic_ios(const std::basic_ios<char,
std::char_traits<char> >&)':
/usr/include/c++/4.0.0/bits/ios_base.h:779: error:
'std::ios_base::ios_base(const std::ios_base&)' is private
/usr/include/c++/4.0.0/iosfwd:55: error: within this context
/usr/include/c++/4.0.0/iosfwd: In copy constructor
'std::basic_istream<char, std::char_traits<char> >::basic_istream(const
std::basic_istream<char, std::char_traits<char> >&)':
/usr/include/c++/4.0.0/iosfwd:61: warning: synthesized method
'std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)' first required here
a.cpp: In function 'int main(int, char**)':
a.cpp:10: warning: synthesized method 'std::basic_istream<char,
std::char_traits<char> >::basic_istream(const std::basic_istream<char,
std::char_traits<char> >&)' first required here
a.cpp:10: error: no matching function for call to
'std::istream_iterator<char, char, std::char_traits<char>,
ptrdiff_t>::istream_iterator(std::basic_istream<char,
std::char_traits<char> >)'
/usr/include/c++/4.0.0/bits/stream_iterator.h:70: note: candidates are:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(const std::istream_iterator<_Tp, _CharT,
_Traits, _Dist>&) [with _Tp = char, _CharT = char, _Traits =
std::char_traits<char>, _Dist = ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:66: note:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(std::basic_istream<_CharT, _Traits>&) [with _Tp
= char, _CharT = char, _Traits = std::char_traits<char>, _Dist = ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:62: note:
std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator()
[with _Tp = char, _CharT = char, _Traits = std::char_traits<char>, _Dist
= ptrdiff_t]

Now, I have discovered that if I change the program into the following,
then it compiles fine:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
istream *ifile = argc >= 2 ? new ifstream(argv[1]) : &cin;
copy(istream_iterator<char>(*ifile),
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

I know this works, but it would be nice to understand why the original
version does not work anyway.

Thank you for your help.

As "? :" need the second and third expression to be of the same type

The first example is trying to cast "ifstream(argv[1])" to "ostream&".
But "ifstream(argv[1])" is an rvalue, you can't cast it to lvalue,
Unless "Rvalue Reference" is allowed
 
V

Victor Bazarov

Barry said:
[..]
As "? :" need the second and third expression to be of the same type

I believe it's not that strict.

long double a = rand() > 5 ? 42 : 3.14159;

Here 42 is 'int' and 3.14159 is 'double'. The requirement is that
the types of the expressions should be such that either the second
can be converted to the first or vice versa.
The first example is trying to cast "ifstream(argv[1])" to "ostream&".

To 'ostream&'? You mean, to 'istream&', right?
But "ifstream(argv[1])" is an rvalue, you can't cast it to lvalue,
Unless "Rvalue Reference" is allowed

V
 
K

Kira Yamato

Kira said:
I've posted this in another thread, but I suppose I should've started
a new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),

'istream_iterator's constructor that accepts a stream object takes the
argument by non-const reference. A non-const reference cannot be bound
to a temporary.

I see. So in C++, temporary variables are always treated as const?

If so, is there a good reason why the C++ designer chose it this way?
As far as I know, a temporary object lives on the stack, and there
should be no reason why it should not be modified.

Thanks.
 
K

Kira Yamato

I've posted this in another thread, but I suppose I should've started a
new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),
// this line won't compile!
istream_iterator<char>(),
ostream_iterator<char>(cout));

return 0;
}

The compiler error messages are as followed:

/usr/include/c++/4.0.0/iosfwd: In copy constructor
'std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)':
/usr/include/c++/4.0.0/bits/ios_base.h:779: error:
'std::ios_base::ios_base(const std::ios_base&)' is private
/usr/include/c++/4.0.0/iosfwd:55: error: within this context
/usr/include/c++/4.0.0/iosfwd: In copy constructor
'std::basic_istream<char, std::char_traits<char> >::basic_istream(const
std::basic_istream<char, std::char_traits<char> >&)':
/usr/include/c++/4.0.0/iosfwd:61: warning: synthesized method
'std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)' first required here
a.cpp: In function 'int main(int, char**)':
a.cpp:10: warning: synthesized method 'std::basic_istream<char,
std::char_traits<char> >::basic_istream(const std::basic_istream<char,
std::char_traits<char> >&)' first required here
a.cpp:10: error: no matching function for call to
'std::istream_iterator<char, char, std::char_traits<char>,
ptrdiff_t>::istream_iterator(std::basic_istream<char,
std::char_traits<char> >)'
/usr/include/c++/4.0.0/bits/stream_iterator.h:70: note: candidates are:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(const std::istream_iterator<_Tp, _CharT,
_Traits, _Dist>&) [with _Tp = char, _CharT = char, _Traits =
std::char_traits<char>, _Dist = ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:66: note:
std::istream_iterator<_Tp, _CharT, _Traits,
_Dist>::istream_iterator(std::basic_istream<_CharT, _Traits>&) [with
_Tp = char, _CharT = char, _Traits = std::char_traits<char>, _Dist =
ptrdiff_t]
/usr/include/c++/4.0.0/bits/stream_iterator.h:62: note:
std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator()
[with _Tp = char, _CharT = char, _Traits = std::char_traits<char>,
_Dist = ptrdiff_t]

Just a side comment here: One reason I found STL extremely difficult
to get into is that because of the extreme difficulty in figuring out
what the error messages are saying.

Even after it compiles fine, I can't begin to imagine how difficult it
is to debug a STL heavy program.
 
K

Kai-Uwe Bux

Kira said:
Kira said:
I've posted this in another thread, but I suppose I should've started
a new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),

'istream_iterator's constructor that accepts a stream object takes the
argument by non-const reference. A non-const reference cannot be bound
to a temporary.

I see. So in C++, temporary variables are always treated as const?

No, and Victor did not say that.

Temporaries cannot be used to initialize non-const references. That does not
imply that temporaries are const objects. You can call non-const member
functions on temporaries. For temporaries of class type, that includes even
the assignment operator (if it is accessible). So, temporaries are by no
means const (unless you created them const). Moreover, any non-const
reference returned by a member function (e.g., the assignment operator)
will happily bind to a non-const reference.

The following snippet illustrates the first point about temporaries being
non-const:

#include <iostream>

struct demo {

demo ( void ) {}

void print ( char const * msg ) {
std::cout << "non-const: " << msg << '\n';
}

void print ( char const * msg ) const {
std::cout << "const: " << msg << '\n';
}

};

typedef demo const const_demo;

int main ( void ) {
demo obj;
obj.print( "object");
const_demo c_obj;
c_obj.print( "const object" );
demo().print( "temporary" );
const_demo().print( "const temporary" );
}


If so, is there a good reason why the C++ designer chose it this way?

It helps avoiding some issues arising from integral promotion. Otherwise, it
is just a nuisance. For instance, you can do:

MyClass & operator= ( MyClass const & other ) {
MyClass( other ).swap( *this );
return ( *thid );
}

but not

MyClass & operator= ( MyClass const & other ) {
this->swap( MyClass( other ) );
return ( *this );
}


You also cannot use a temporary to initialize a default non-const parameter:

void search ( SearchClass & initial_pos = SearchClass() );

but if you provide a member function

class SearchClass {
...

SearchClass & me ( void ) {
return ( *this );
}

};

you can do:

void search ( SearchClass & initial_pos = SearchClass().me() );

And for standard classes that do not support a me() method, you could do:

void grow ( std::vector<int> & the_list =
( std::vector<int>() = std::vector<int>() ) );

Since the assignment operattor returns a non-const reference, the result
will happily bind to the parameter.

As you can see, it is not very logical at all.


The next version of the standard will include r-value references which
hopefully will put an end to this nonsense.


As far as I know, a temporary object lives on the stack, and there
should be no reason why it should not be modified.

There isn't and you can modify temporaries at will. You just cannot bind
them to non-const references directly.



Best

Kai-Uwe Bux
 
V

Victor Bazarov

Kira said:
Kira said:
I've posted this in another thread, but I suppose I should've
started a new thread for it instead.

I cannot get the following short program to compile under g++:

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>

using namespace std;

int main(int argc, char **argv)
{
copy(istream_iterator<char>(argc >= 2 ? ifstream(argv[1]) : cin),

'istream_iterator's constructor that accepts a stream object takes
the argument by non-const reference. A non-const reference cannot
be bound to a temporary.

I see. So in C++, temporary variables are always treated as const?

No. There is no connection. A non-const reference cannot be bound
to a temporary. But a temporary is not a constant object.
If so, is there a good reason why the C++ designer chose it this way?

It isn't so.
As far as I know, a temporary object lives on the stack, and there
should be no reason why it should not be modified.

Where they live is unspecified.

V
 
P

Pete Becker

Just a side comment here: One reason I found STL extremely difficult
to get into is that because of the extreme difficulty in figuring out
what the error messages are saying.

Yup. It's hard. It helps to practice: write code with deliberate errors
and see what your compiler gives you for error messages. In the next
revision of the standard, "concepts" will improve this situation
considerably. Template writers will be able to decorate them with
information about what the template requires from its arguments, so the
compiler can give better error messages.
Even after it compiles fine, I can't begin to imagine how difficult it
is to debug a STL heavy program.

Debugging is usually straightforward. Those layers of templates usually
collapse into fairly simple code.
 
K

Kira Yamato

It helps avoiding some issues arising from integral promotion. Otherwise, it
is just a nuisance. For instance, you can do:

MyClass & operator= ( MyClass const & other ) {
MyClass( other ).swap( *this );
return ( *thid );
}

but not

MyClass & operator= ( MyClass const & other ) {
this->swap( MyClass( other ) );
return ( *this );
}


You also cannot use a temporary to initialize a default non-const parameter:

void search ( SearchClass & initial_pos = SearchClass() );

but if you provide a member function

class SearchClass {
...

SearchClass & me ( void ) {
return ( *this );
}

};

you can do:

void search ( SearchClass & initial_pos = SearchClass().me() );

And for standard classes that do not support a me() method, you could do:

void grow ( std::vector<int> & the_list =
( std::vector<int>() = std::vector<int>() ) );

Since the assignment operattor returns a non-const reference, the result
will happily bind to the parameter.

As you can see, it is not very logical at all.


The next version of the standard will include r-value references which
hopefully will put an end to this nonsense.




There isn't and you can modify temporaries at will. You just cannot bind
them to non-const references directly.



Best

Kai-Uwe Bux

Thank you for your very detailed explanation.

It's beginning to dawn on me that many rules in C++ can be bent.

For example, your code above shows a way to "bind" a temporary object
to a non-const reference (by using the returned value of the operator =
).

In another thread I started last week, someone showed me how to declare
a const member function that can modify its own member variable without
using 'volatile' nor 'const_cast'. Essentially, his code was as follow:

class Obj
{
private:
Obj *that;
int state;

public:
Obj() : that(this), state(0) {}

void changeMe() const
{
that->state++; // changing state!
}
};
 
K

Kai-Uwe Bux

Kira said:
Thank you for your very detailed explanation.

It's beginning to dawn on me that many rules in C++ can be bent.

For example, your code above shows a way to "bind" a temporary object
to a non-const reference (by using the returned value of the operator =
).

Be careful when bending the rules :)

For instance, binding a temporary to a non-const reference is subject to
livetime issues. The following is not good:

int_vector & iv = ( int_vector() = int_vector() );

The reason is that the temporary int_vector is destroyed right away. For
const-references, the standard makes a special guarantee that the temporary
that is bound to the reference (which may not be the one you think it is)
lives as long as the reference.

The reason that you can use these tricks to bind a temporary to a parameter
in a function call expression is that the temporary is guaranteed to live
as long as the maximal enclosing expression. As for initializing the
default parameter, I am actually not sure (I think I checked it once and
convinced myself that it is OK, but my memory might play a prank on me).

In another thread I started last week, someone showed me how to declare
a const member function that can modify its own member variable without
using 'volatile'

you mean 'mutable'
nor 'const_cast'. Essentially, his code was as follow:

class Obj
{
private:
Obj *that;
int state;

public:
Obj() : that(this), state(0) {}

void changeMe() const
{
that->state++; // changing state!
}
};

Now, that is evil (and if the object was actually declared const, it is also
undefined behavior).


Best

Kai-Uwe Bux
 
J

James Kanze

Barry said:
[..]
As "? :" need the second and third expression to be of the same type
I believe it's not that strict.

Not at all. Ignoring the special case where one of the second
or third expressions is a throw expression, the rule for class
types is that one one of the types must convert to the other.
One typical use is initializing a pointer to Base with one of
two different derived, and this requires an explicit cast, since
there is no attempt to convert both to some common third type.
long double a = rand() > 5 ? 42 : 3.14159;
Here 42 is 'int' and 3.14159 is 'double'. The requirement is that
the types of the expressions should be such that either the second
can be converted to the first or vice versa.

I'ts a bit more complicated than that:). In the case of
arithmetic or enumeration types, the "usual arithmetic
conversions" rule applies. Otherwise, of course, the above
would be ambiguous: int converts to double, and double converts
to int.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top