C++ : cout with a terenary if-statement
Asked Answered
D

7

10

I get this ERROR: "error: overloaded function with no contextual type information".

cout << (i % 5 == 0) ? endl : "";

Is what I am doing possible; am I just doing it wrong, or do I have to overload the << operator?

Dorena answered 20/4, 2011 at 6:38 Comment(17)
Why not just if (i%5==0) cout << endl;?Fireboat
Because, he want to use a ternary operator.Nystagmus
@kalyan: That's begging the question. "How can I do X?" "Why X?" "Because I want X". Doesn't answer the question: Why use a conditional expression here? It's ugly, overly-complicated, and won't ever actually work.Angadresma
@GMan: in this case, there's something that's very similar functionally (and probably what was intended given new users rarely understand that endl flushes and when you need to care). Using that style, I do often find myself using ternary operators to select parts of strings. "ugly, over-complicated"? IMHO, it's concise and nicely localised, readable and overall good code style. C/C++ differentiates itself from pascal etc. by providing this level of concise expressiveness... and I'm mighty glad.Robbegrillet
@Tony: Adding "" to cout doesn't have any relevant effect, so why do it at all in this case?Fireboat
@Tony: What do other cases have to do with this case? I'm saying this use is ugly and over-complicated. Consider this expression verses Georg's: which is easier to read?Angadresma
@Georg: there are two factors to consider. First, std::cout << ... emphasises printing over what and whether. If you're not interested in printing, you immediately know to ignore this line. if (expr) statement; forces you to scan along the line to see if it's of interest. Second, not in this case but in a vast number, it's used in part of a larger output like for (...each element...) std::cout << element << (is_last_expr ? "" : ", ");: it's often messier (source code readability/concision wise, needing braces) having an extra statement just to get a tiny improvement in efficiency.Robbegrillet
@GMan: we ask (sometimes have to beg almost :-)) people to post minimal sample code that illustrates their question, so don't take it too literally - as if this was the only and exact use case and all related considerations are irrelevant.Robbegrillet
@Tony: The rest of the code is actually quite irrelevant as well. It's truly just this expression that is terrible code. A superior method exists. The conditional expression is, conceptually, an if-then-else expression, but the problem at hand is only an if-then; no -else. Wrong tool for the job, period.Angadresma
@GMan: thanks for the "period"... so few absolutes in programming it's good to know where to come when I want to look at one ;-).Robbegrillet
@Tony: Do you have a rebuttal, or do you always just ignore things when you don't want to answer them?Angadresma
@GMan: I'm sorry - I didn't realise you'd made a new point. "rest of the code is...irrelevant" - is that about my comment that this might be a simplification? I've illustrated above how std::cout << always << (expr ? ", " : ""); a) emphasises output and deemphasises the separator which is a truly trivial detail. Did you respond to that (perhaps the claim of "irrelevant")? "It's truly just this expression that is terrible"? Do you mean "this statement"?, i.e. agree usage like I've described might make sense but the exact case posted is indefensible? Have you some other point?Robbegrillet
@Tony: I said he wants an if-then, and ternary is if-then-else; a mismatch, ergo not the right approach.Angadresma
@GMan: I don't see that being significantly different to a lot of other places where people choose to have no-ops/empty-statements or fail to exercise intended support for branching for stylistic reasons, e.g. while (true) { ... if (x) break; }, while (expr) ;, for (;;) statement;. Sometimes too, the exact strings may be provided by macros, code generation, template policies etc. where it may not be known or guaranteed whether they're empty, and ? "x" : "" is just the way it pans out.Robbegrillet
@Tony: And again, when those other cases arise, we'll deal with them. That isn't the case here.Angadresma
@GMan: Agreed, and round we go. It's normal on S.O. to try to cover the problem space a little, rather than stick to the exact question. For the exact usage posted here (ignoring the fact it won't compile) I agree that the stylistic aspects I mention are practically never justification. I've never disputed that. Are you disputing that in other streaming statements, conditional values where one is "" can be useful, or just maintaining that I shouldn't be diverging the discussion so wildly off topic? ;-)Robbegrillet
@Tony: Well then we're set, it's a bad idea to try to get this to work in this situation. If you'd like to bring up the issue of other uses, you can do so, but it's probably best to deal with them when they arise.Angadresma
P
34

It won't work that way (even if you fix the the precedence error). You have two problems here, the second more severe than the first.

The first problem is that std::endl is a template. It is a function template. A template has to be specialized. In order to specialize that template the compiler has to know (to deduce) the template arguments. When you do

std::cout << std::endl;

the specific function pointer type expected by operator << is what the compiler uses to figure out how to specialize the std::endl template.

However in your example you essentially "detached" the std::endl from operator << by moving the std::endl into an ?: subexpression. Now the compiler has to compile this expression first

(i % 5 == 0) ? endl : ""

This expression cannot be compiled since the compiler does not know how to specialize the std::endl template. There's no way to deduce the template arguments without any context.

For example, this simple C++ program

#include <iostream>
int main() {
   std::endl;
}

will also fail to compile for the very same reason: without context the compiler does not know how to instantiate std::endl.

You can "help" the compiler to resolve the problem by specifying the template arguments explicitly

(i % 5 == 0) ? endl<char, char_traits<char> > : "";

This will explicitly tell compiler how to instantiate endl. The original error message you were getting will go away.

However, this will immediately reveal the second, more serious problem with that expression: specialized endl is a function (which decays to a function pointer in this context) while "" is a string literal. You cannot mix a function pointer and a string literal in a ?: operator like that. These types are incompatible. They cannot be used together as the 2nd and the 3rd operand of ternary ?:. The compiler will issue a different error message about this second problem.

So, basically, that latest problem you have here is as if you tried to do something like

cout << (i % 5 == 0 ? 10 : "Hi!");

This will not compile for the very same reason your expression will not compile.

So, the expression you are trying to write cannot be written that way. Rewrite it without trying to use the ?: operator.


As support, see the following transcript:

$ cat qq.cpp
#include <iostream>
using namespace std;
int main (void) {
    int i = 5;
    cout << ((i % 5 == 0) ? endl : "");
    return 0;
}

$ g++ -o qq qq.cpp
qq.cpp: In function 'int main()':
qq.cpp:5: error: overloaded function with no contextual type information
Publican answered 20/4, 2011 at 6:50 Comment(2)
+1 And if it were explicitly selected, then it would still fail because a function pointer and string literal have nothing in common.Angadresma
covers a lot of ground I lacked patience for. +1.Robbegrillet
R
15

The two arguments to the ? operator must be of the same type (at least after potential promotions, implicit constructors, casting operators etc. kick in). std::endl is actually a function template (details below) which the stream then invokes to affect its state: it is not a string literal like "".

So, you can't do this exactly, but you can probably get the behaviour you actually want - consider whether...

expr ? "\n" : ""

...meets your needs - it is similar but doesn't flush the stream (IMHO, std::cout should generally be flushed as infrequently as possible - especially by low level library code - as that provides better performance). (It's also more flexible, e.g. expr ? "whatever\n" : "" / can't append endl to a string literal.)

E.g. for GCC 4.5.2, endl is:

template<typename _CharT, typename _Traits>
    inline basic_ostream<_CharT, _Traits>& 
    endl(basic_ostream<_CharT, _Traits>& __os)
    { return flush(__os.put(__os.widen('\n'))); }
Robbegrillet answered 20/4, 2011 at 6:47 Comment(1)
I upvoted AndreyT for his explanations, but +1 for actually providing a solution :)Goldoni
S
3
  • The two alternatives of ?: must have the same type or one be convertible to the other.

  • endl is a template and the context doesn't give enough information for which to choose. So it hasn't even a type. (Thats is your error message).

  • As other already said, the binding isn't the one you expect.

Stabilize answered 20/4, 2011 at 6:47 Comment(0)
W
2

It may well be possible (I doubt it myself). However, it's also silly, effectively as silly as:

cout << "";

What you should be doing in this case is a simple:

if (i % 5 == 0) cout << endl;

You shouldn't use ternary just for the sake of using it. Actually you shouldn't use any language feature just for the sake of using it. I don't write code like:

if (1) { doSomething(); }

just because I can. A simple doSomething(); is much better.

Whitman answered 20/4, 2011 at 6:53 Comment(0)
S
2

Try this, it works:

cout << ((i % 5 == 0) ? "\n" : "");
Soteriology answered 20/1, 2017 at 0:35 Comment(0)
D
1

This is how it is supposed to look like to make it work correctly:

cout << ((i % 5 == 0) ? '\n' : " ");
Derbent answered 13/9, 2017 at 15:23 Comment(0)
M
-1

Operator << has higher priority than ?:. Try this:

cout << ((i % 5 == 0) ? endl : "");
Miguel answered 20/4, 2011 at 6:41 Comment(2)
Did you try it yourself? Will it compile?Robbegrillet
Time to look back at old stuff?Ludovika

© 2022 - 2024 — McMap. All rights reserved.