5

I just stumbled over this piece of code:

std::string export_str = "/path/to/file";
std::ofstream export(export_str.c_str());
if (export < 0) {
    std::cout << "Unable to export" << std::endl;
    return -1;
}

This compiles and runs fine with GCC 4.9.3 but on GCC 6.1.1 this error comes up:

error: no match for ‘operator<’ (operand types are ‘std::ofstream {aka std::basic_ofstream<char>}’ and ‘int’)
 if (export < 0) {
     ~~~~~~~~~~~^~~

I tried GCC 6 with:
-std=c++98 (does compile)
-std=c++03 (does compile)
-std=c++11 (does not compile)

Edit: However, in GCC 4 it still compiles with -std=c++11. This specific fact is also explained in the answer below. :)

So I guess there was a change in the standard regarding this.

After some research I changed the code to:

std::string export_str = "/path/to/file";
std::ofstream export(export_str.c_str());
if (export.fail()) { // <-- related change
    std::cout << "Unable to export" << std::endl;
    return -1;
}

This compiles and runs fine but I did not find a good explanation for this change, probably due to not coming up with a good search term combination.

So my question is not "How to check validity of ofstream". There are quite some more or less satisfying answers already ("more or less" because the issue seems to be a bit complicated).
Here or here or here.

My question is for an explanation for the change made between GCC 4 and GCC 6 regarding compiling things like (export < 0) in the above code.

Thank you for any pointers.

  • What do you expect `export < 0` to mean? – juanchopanza Jun 04 '16 at 10:57
  • the silly person wrote `if (export < 0)` instead of the C-ish `if (export)` construct - it's not only obfuscated, but also longer; why they did this, we'll probably never know... –  Jun 04 '16 at 11:02
  • 3
    May I stronly encourage you not to create a variable called export. In c++11 it is unused, but it is still reserved. – Tommy Andersen Jun 04 '16 at 11:02
  • @juanchopanza: I did not write the code. It looks to me like C-ish C++98 code. I think it was the length (lexical comparison). But I did not investigate that because if the compiler doesn't like it so I don't like it either. :) –  Jun 04 '16 at 11:40
  • @TommyAndersen: Meh, sorry. I simplified the naming after copying it here and did not think of reserved keywords. The original code has different names, though. +1 for being observant. :) –  Jun 04 '16 at 11:43
  • @Bugfinger OK. Well, the code is simply wrong. `export < 0` doesn't make any sense. I would treat that code base with caution. – juanchopanza Jun 04 '16 at 15:25
  • 1
    @juanchopanza: I do. I am trying to fix things like that and in general modernise the code ... I just want to understand the things that do not seems to make sense. –  Jun 04 '16 at 15:38

1 Answers1

12

Pre-C++11, the standard streams are implicitly convertible to void*, with NULL indicating a bad stream and non-NULL a good stream.

So what you get is a pointer comparison between (void*)export and (void*)0, which is both legal (in the "should compile" sense) and non-sensical.

In C++11, the stream conversion to void* was replaced by an explicit conversion to bool, which still allows to check the state of the stream as before, but makes nonsense code such as yours illegal.

The important thing here is the change from implicit to explicit conversion btw. If the new conversion to bool was implicit, the code would still compile and do a (bool) export < 0 comparison. But with the explicit conversion, that would require a cast.


Regarding the difference between gcc4 and gcc6: The streams in libstdc++ 4.x are not C++11 conforming in this regard. The C++11 stream conversion, along quite a few C++11 shortcomings including move semantics for streams and SSO, was fixed/implemented in version 5.

gcc4 just isn't C++11 feature complete, in this case, it follows the old rules where it shouldn't.


Just for completeness' sake: As already mentioned in the comments, export is a keyword and should not be used as a name.

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
  • Thanks, that explains it to me. Regarding C++11 feature-non-completeness of GCC 4, I might add that indeed with `g++-4.9 -std=c++11` the old code does compile. I forgot to state that in my question. –  Jun 04 '16 at 11:50