Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does clang let me take a temporary by non-const reference in C++03 mode?

Tags:

c++

clang

Inspired by my observation in a previous question, I decided to do a little test:

#include <iostream>
#include <sstream>

int main()
{
  char c = 'A';
  std::stringstream ss("B");

  // I know this is bad mojo; that's why I'm testing it
  ss >> char(c);

  std::cout << c << std::endl;
}

My compiler version:

Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix

Compiling clang in C++03 mode, it compiles and runs okay:

$ clang++ -Wall -pedantic -std=c++03 test.cpp 
test.cpp:9:6: warning: expression result unused [-Wunused-value]
  ss >> char(c);
  ~~ ^  ~~~~~~~
1 warning generated.
$ ./a.out 
A

It prints out A, which is fine, given that this code shouldn't even compile. Switching to C++11 mode, it properly errors when compiling (error: invalid operands to binary expression ('std::stringstream' (aka 'basic_stringstream<char>') and 'int')).

Yes, in C++03 mode it does give a warning, but it's not a warning I'd expect (I expected some kind of "taking temporary by reference" warning/error, or perhaps a warning/error saying no operator>> accepts a char parameter).

My question is: why does the code successfully compile in C++03 mode? Is it using some alternative overload of operator>> that avoids taking the temporary by reference? Is it being overly lax and letting me take the temporary by reference? I'm puzzled at why clang accepts this code at all; GCC 4.9 properly errors out in C++03/11 modes with a much more relevant error (error: no match for 'operator>>' (operand types are 'std::stringstream {aka std::basic_stringstream<char>}' and 'char')). Clang's behavior in C++03 mode is puzzling me.

like image 364
Cornstalks Avatar asked Sep 13 '14 14:09

Cornstalks


1 Answers

I think it invokes the operator bool() of std::stringstream, that is, the code is interpreted as

bool(ss) >> char(c);

which of course is a valid statement with no effect. The implicit conversion to bool returns !fail() and is what allows code like

while (ss >> foo)
  ...

The standard allows to convert to another type that converts to bool instead of plain bool (for example a void*) exactly to avoid this type of problem. Apparently your implementation does not make use of that freedom.

like image 154
celtschk Avatar answered Sep 30 '22 08:09

celtschk