0

I'm trying to clean up some code with ternary operators and I'm running into some compiler errors i can't make sense of.

The code I had before looks like this and runs fine.

if(!inFile.good())
     throw -2;
getline(inFile, inLine);

And I'm trying to clean it up using this code instead.

(inFile.good()) ? getline(inFile, inLine) : throw -2;

But I'm getting the following errors.

g++ -w -o test orange_test.cpp
In file included from orange_test.cpp:4:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/iostream:39:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/streambuf:558:31: error: base class 'std::__1::ios_base' has private
  copy constructor
_LIBCPP_EXTERN_TEMPLATE(class basic_ios<char>)
                          ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__config:462:54: note: expanded from macro '_LIBCPP_EXTERN_TEMPLATE'
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
                                                 ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/ios:305:5: note: declared private here
ios_base(const ios_base&); // = delete;
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/istream:1706:31: note: implicit default copy constructor for
  'std::__1::basic_ios<char>' first required here
_LIBCPP_EXTERN_TEMPLATE(class basic_istream<char>)
                          ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/__config:462:54: note: expanded from macro '_LIBCPP_EXTERN_TEMPLATE'
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
                                                 ^
./orange_model.hpp:145:23: note: implicit default copy constructor for 'std::__1::basic_istream<char>' first required here
                            (inFile.good()) ? getline(inFile, inLine) : throw -2;
                                              ^
1 error generated.
make: *** [main] Error 1

I can't make any sense of it. Is there some limitation of ternary operators or scope that I'm not aware of? Any help or insight would be greatly appreciated. Thanks in advance, Max

Edit

From looking at it the main error seems to be

error: base class 'std::__1::ios_base' has private copy constructor

It's complaining about the std::getline(inFile, inLine) function.

Answer

This code gets it running smoothly. Thanks everyone!

inFile.good() ? (void) getline(inFile, inLine) : throw -2;
Max Rahm
  • 684
  • 11
  • 25
  • 3
    Changing from an `if` into the conditional operator would be considered by many (including me) the opposite of cleanup. The code is more obscure with the use of the conditional operator than it is with the original `if`. And this rule applies to almost all uses of the conditional operator that are not *select one of these **two** values* – David Rodríguez - dribeas Jan 02 '14 at 05:26

3 Answers3

6

The ternary operator has a special rule when one side is a throw. Part of that rule is that the result always will be a temporary rvalue, never a reference. So the compiler needs to make a copy of the getline() return value, and this fails because the stream is non-copyable.

You can explicitly discard the getline() return value in order to avoid the copy attempt:

inFile? (void)getline(inFile, inLine) : (throw -2);

I don't agree with your cleanup attempt, however. The original is easier to understand and maintain.

Also, did you mean to test goodness of inFile before or also after getline()? You could do

getline(inFile, inLine) || (throw -2);

which is idiomatic is some other languages, like perl's something or die "Error message"

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 1
    The other (valid) side becomes a prvalue indeed: http://stackoverflow.com/questions/20848132/can-an-exception-be-thrown-from-the-ternary-operator, that's why it gets copied. – Jack Jan 02 '14 at 04:52
2

If you're set on the ternary conditional (which I personally don't agree with), you have to prevent the throw() part from causing a copy (see Ben's answer), which you can do by making that part evaluate to a reference, just as the getline part does, with the comma operator:

inFile.good() ? getline(inFile, inLine) : (throw -2, inFile);

Where the error comes in is that the copy-constructor for std::istream is private to prevent copying, causing that attempted copy to fail. Just having the throw without the comma operator would work with, e.g., 1 instead of the getline call.

getline returns the istream & you pass in (inFile), and the last part evaluates the throw, discards the result, and evaluates and uses inFile as its final value. Except here once you do the throw, execution does not continue to evaluating inFile, so it won't have an effect on the running code's logic.

chris
  • 60,560
  • 13
  • 143
  • 205
  • What exactly is the type of a `throw` expression's result? – Joker_vD Jan 02 '14 at 04:47
  • @Joker_vD, `void`. See [this question](http://stackoverflow.com/questions/1212978/in-c-if-throw-is-an-expression-what-is-its-type). – chris Jan 02 '14 at 04:47
  • @Joker_vD: it is a statement, it doesn't have a return type. It's like asking the type of `while`. – Jack Jan 02 '14 at 04:48
  • 1
    The ternary operator has a special rule when one side is a `throw`. A `throw` is compatible with any type. It does subtly influence the result type, however. So the comma operator can work around that. – Ben Voigt Jan 02 '14 at 04:49
  • @chris Thanks. I've learned yet another ad-hoc semantic hot-fix in C++. – Joker_vD Jan 02 '14 at 04:52
  • @BenVoigt, Thanks, I hope my new wording is better without intruding on your answer. – chris Jan 02 '14 at 04:53
  • 1
    What about `inFile.good() ? (void)getline(inFile, inLine) : throw -2;` ? – Ben Voigt Jan 02 '14 at 04:56
  • @BenVoigt, Good one. That's probably better than the comma operator, and the logical OR even better imo. The last is the only one that reasonably stands up to the if statement. Seems your answer is accepted now, so even better for the more reasonable methods showing up. – chris Jan 02 '14 at 05:01
  • @chris the logical OR does change the meaning though, it tests whether the stream is (still) good *after* the `getline()` – Ben Voigt Jan 02 '14 at 05:03
  • @BenVoigt, That's true, but it could be used with the operation *before* the read, and is indeed pretty idiomatic elsewhere, though I can't say I've seen much of it in C++. – chris Jan 02 '14 at 05:04
-1

You are not supposed to use the "return-value" of a throw.

rom1504
  • 27
  • 4