|  | >	#include <iostream.h>
>	int main(int argc, const char* argv[])
>	{
>	   unsigned char uc = 0xeb;
>	   unsigned char ucOut1 =  uc<<4;
>	   unsigned char ucOut2 = (uc<<4)>>4;
>	   cout<<hex<<unsigned(ucOut1)<<' '<<unsigned(ucOut2)<<endl;
>	      // prints "b0 eb" while it should print "b0 0b"
>	   return 0;
>	}
        
	The short answer is that the "b0 eb" output is correct,
        because in the expression
        
		(uc<<4)>>4
        
        the unsigned char uc is promoted to int and the expression is
        evaluated as "(int << int) >> int" with an int result.
        
	The long answer points to both:
        
        	usual arithmetic conversions
        	integral promotions
        
        First, from the DWP on usual arithmetic conversions:
        
9 Many binary operators that expect operands of arithmetic  or  enumera-
  tion  type  cause conversions and yield result types in a similar way.
  The purpose is to yield a common type, which is also the type  of  the
  result.   This  pattern  is  called  the usual arithmetic conversions,
  which are defined as follows:
  --If either operand is of type long double, the other  shall  be  con-
    verted to long double.
  --Otherwise, if either operand is double, the other shall be converted
    to double.
  --Otherwise, if either operand is float, the other shall be  converted
    to float.
  --Otherwise,  the integral promotions (_conv.prom_) shall be performed
    on both operands.1)
  --Then,  if  either  operand  is unsigned long the other shall be con-
    verted to unsigned long.
  --Otherwise, if one operand is a long int and the other unsigned  int,
    then  if a long int can represent all the values of an unsigned int,
    the unsigned int shall be converted to a long  int;  otherwise  both
    operands shall be converted to unsigned long int.
  --Otherwise,  if  either operand is long, the other shall be converted
    to long.
  --Otherwise, if either operand is unsigned, the other  shall  be  con-
    verted to unsigned.
  [Note:  otherwise,  the  only remaining case is that both operands are
  int ]
  _________________________
  1) As a consequence, operands of type bool, wchar_t, or an  enumerated
  type are converted to some integral type.
        
        Next, from the DWP on integral promotions:
        
  4.5  Integral promotions                                   [conv.prom]
1 An  rvalue  of  type  char,  signed char, unsigned char, short int, or
  unsigned short int can be converted to an rvalue of type  int  if  int
  can represent all the values of the source type; otherwise, the source
  rvalue can be converted to an rvalue of type unsigned int.
2 An rvalue of type wchar_t (_basic.fundamental_) or an enumeration type
  (_dcl.enum_) can be converted to an rvalue of the first of the follow-
  ing types that can represent all the values of  its  underlying  type:
  int, unsigned int, long, or unsigned long.
3 An  rvalue for an integral bit-field (_class.bit_) can be converted to
  an rvalue of type int if int can represent all the values of the  bit-
  field;  otherwise, it can be converted to unsigned int if unsigned int
  can represent all the values of the bit-field.  If  the  bit-field  is
  larger yet, no integral promotion applies to it.  If the bit-field has
  an enumerated type, it is treated as any other value of that type  for
  promotion purposes.
4 An rvalue of type bool can be converted to an rvalue of type int, with
  false becoming zero and true becoming one.
5 These conversions are called integral promotions.
        
        
    
>	Without converting an unsigned char variable to unsigned() in the
>	streaming, the output would treat it as a char (as opposed to an
>	integral value) and print a graphic of 'e' with two dots on its
>	top. Is this another bug, or does C++ standard leave it to
>	implementation, or is this exactly as specified by the standard?
        The arithmetic inserters are...
        
        
  27.6.2.5.2  Arithmetic              [lib.ostream.inserters.arithmetic]
       Inserters
  operator<<(bool val);
  operator<<(short val);
  operator<<(unsigned short val);
  operator<<(int val);
  operator<<(unsigned int val);
  operator<<(long val);
  operator<<(unsigned long val);
  operator<<(float val);
  operator<<(double val);
  operator<<(long double val);
  operator<<(void* val);
        The inserters for char, signed char, and unsigned char are not
        arithmetic inserters.  Instead they all insert the specified
        character to the output stream as a character, not as a
        byte-sized integer.  If they want to format the integral value
        of uc or ucOut1 or ucOut2 then they do need the cast as they
        already used in their example.
        
        Dan
 |