which compiler is following the standard?

E

eduzea

I have some code that compiles with gcc but fails with MS VC 8. I
would like to know which one is following the C++ standard.

istream& istr = false ? cin : ( * new ifstream("test.txt")) ;

Using g++ it compiles. Using MS VC 8 it fails with a message along the
lines of:

"cannot access private member declared in class
'std::basic_ios<_Elem,_Traits>' "

The code below however,

istream& istr = * new ifstream("test.txt") ;

compiles fine with MS VC8.

It seems it has to do with the outcome of the ?: operator, which is
generating a call to the copy constructor in the MS compiler but not
in g++ ...?

Thanks,

Eduardo
 
M

Marcel Müller

Hi,

I have some code that compiles with gcc but fails with MS VC 8. I
would like to know which one is following the C++ standard.

istream& istr = false ? cin : ( * new ifstream("test.txt")) ;

your code looks wrong. Who is going to delete the ifstream instance? No
one I guess.

For similar purposes I wrote smart pointers that can store owned objects
and static or in general unowned object as well. Some of the boost smart
pointers will do the job too, but they require to pass an empty custom
deleter each time you want to store an unowned object.

I prefer the syntax:

my_smart_ptr<istream> istr;
if (condition)
istr = cin;
else
istr = new ifstream("test.txt");

In fact another assignment operator is called when a reference is
assgigned rather than a pointer.


Marcel
 
J

Juha Nieminen

istream& istr = false ? cin : ( * new ifstream("test.txt")) ;

Regardless of whether that's valid or not, it looks like a rather bad
idea for several reasons. It leaks memory and the file stream is never
closed.

If you really want to write "hacker" code like that, maybe do it like
this:

std::ifstream fs;
std::istream& istr = false ? std::cin : (fs.open("test.txt"), fs);

(Of course this is a bit obfuscated so it has no other value than to
show off.)
 
J

James Kanze

I have some code that compiles with gcc but fails with MS VC 8. I
would like to know which one is following the C++ standard.
istream& istr = false ? cin : ( * new ifstream("test.txt")) ;
Using g++ it compiles. Using MS VC 8 it fails with a message along the
lines of:
"cannot access private member declared in class
'std::basic_ios<_Elem,_Traits>' "
The code below however,
istream& istr = * new ifstream("test.txt") ;
compiles fine with MS VC8.
It seems it has to do with the outcome of the ?: operator,
which is generating a call to the copy constructor in the MS
compiler but not in g++ ...?

I think it's legal, but it's not a simple case (and
historically, it's unspecified whether it is legal or not). The
problem is that cin and *new ifstream() have different types, so
the compiler must perform a conversion while maintaining
lvalue-ness. (According to the current standard, the type of
cin must be std::istream. Historically, it was allowed to be a
type which derived from std::istream, and if it was, but the
type wasn't ifstream, then the above code is illegal.)

I'd start by static_casting both expressions (cin and the new)
to std::istream&. That ensures that they are lvalues of the
same type, which shouldn't give the compiler any problems.

Don't forget the test when your through, either:

if ( &istr != &cin ) {
delete &istr ;
}

You might consider wrapping the whole thing up in a class.
Something like:

class Input
{
public:
explicit Input( std::istream* source )
: mySource( source == NULL ? &std::cin : source )
{
}

~Input()
{
if ( source != &std::cin ) {
delete source ;
}
}

operator std::istream&()
{
return *mySource ;
}

template< typename T >
istream& operator>>( T& dest )
{
*mySource >> dest ;
return *mySource ;
}

private:
std::istream* mySource ;
} ;

(What you're doing, but with the casts, was a consecrated idiom
in the days before member templates, when you couldn't write the
above, and before exceptions, when you could be fairly sure to
read the if and do the delete. I'm not sure that it's such a
good idea today, given that better idioms exist.)
 
N

Noah Roberts

Juha said:
Regardless of whether that's valid or not, it looks like a rather bad
idea for several reasons. It leaks memory and the file stream is never
closed.

We don't know that. The OP might have some abomination later in the
code. Something like this:

delete &istr;

That will cause a whole different problem if "false" is anything that
might be true.
 
R

Rolf Magnus

Noah said:
We don't know that. The OP might have some abomination later in the
code. Something like this:

delete &istr;

That will cause a whole different problem if "false" is anything that
might be true.

Well, he could just check if istr refers to cin and only delete the stream
if it doesn't. That would still be ugly, but it would work correctly, even
if false becomes true.
 
R

Rajib

I have some code that compiles with gcc but fails with MS VC 8. I
would like to know which one is following the C++ standard.

istream& istr = false ? cin : ( * new ifstream("test.txt")) ;

Using g++ it compiles. Using MS VC 8 it fails with a message along the
lines of:

"cannot access private member declared in class
'std::basic_ios<_Elem,_Traits>' "

The code below however,

istream& istr = * new ifstream("test.txt") ;

compiles fine with MS VC8.

It seems it has to do with the outcome of the ?: operator, which is
generating a call to the copy constructor in the MS compiler but not
in g++ ...?

Thanks,

Eduardo

FWIW, the draft C++ standard says:

For every pair of promoted arithmetic types L and R, there exist
candidate operator functions of the form
24
LR operator?(bool, L , R );
where LR is the result of the usual arithmetic conversions between
types L and R.


For every type T, where T is a pointer or pointer-to-member type,
there exist candidate operator functions of the form
25
T operator?(bool, T , T );


,

so you can't use the ? operator on class types such as ifstream (or
maybe I'm completely wrong on this). If you could, you'd probably need
to be able to access the copy constructor. In any case, you could just
use ? operator on the pointer:

istream& istr = *(false ? &cin : ( new ifstream("test.txt")) );


although you still have all the other problems that others have pointed out.
 
J

James Kanze

FWIW, the draft C++ standard says:
For every pair of promoted arithmetic types L and R, there exist
candidate operator functions of the form
24
LR operator?(bool, L , R );
where LR is the result of the usual arithmetic conversions between
types L and R.

For every type T, where T is a pointer or pointer-to-member type,
there exist candidate operator functions of the form
25
T operator?(bool, T , T );

,
so you can't use the ? operator on class types such as
ifstream (or maybe I'm completely wrong on this).

That's an interesting quote. It comes from the section on
overload resolution, and describes how the built-in operators
interact with user defined operators during overload resolution.
Since you can't overload ?:, I'm not sure that it's relevant to
anything. Section 5.16 describes ?: in detail, including all
relevant conversions. You very definitely can use user defined
types for the second and third operands.

I think that the standard is abiguous here. The second and
third arguments have different types (std::istream and
std::ifstream, respectively), but both are lvalues, and the
expression in the third argument can be implicitly converted to
istream& (lvalue reference to the type of the second argument).
There is a statement "If the second and third operands are
lvalues and have the same type, the result is of that type and
is an lvalue[...]"; it is not clear to me whether the "have the
same type" here applies before or after the above conversion.
(Logically, I would expect that it happens after, which would
make the statement legal.)

Also, historically at least, cin wasn't always an istream, but
rather some class derived from istream. I don't think that the
actual wording in the current standard allows this (although I'm
not sure that it was forbidden intentionally), but it wouldn't
surprise me if some implementations still did have a cin which
was a derived class. If the type of cin is neither a base class
of nor derived from std::ifstream, then the statement definitely
shouldn't compile.

Both problems can be solved simply by casting the results of the
new to std::istream* before dereferencing it.
If you could, you'd probably need to be able to access the
copy constructor. In any case, you could just use ? operator
on the pointer:
istream& istr = *(false ? &cin : ( new ifstream("test.txt")) );

This would solve the lvalue problem, above, if that's the actual
problem, but it still wouldn't work if cin wasn't an
std::istream, but rather a class derived from std::istream.
 

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,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top