0

While studying some of the new C++11 features, I observed some strangeness related to the new decltype keyword and its interaction with the conditional operator.

I was very surprised to see the output of the following program:

#include <iostream>
#include <map>

int main(void)
{
    // set up a map that associates the internal compiler-defined type_info name with a human readable name
    std::map <std::string, std::string> types;
    types[typeid(decltype(static_cast<unsigned char  >(0))).name()] = "unsigned char";
    types[typeid(decltype(static_cast<unsigned short >(0))).name()] = "unsigned short";
    types[typeid(decltype(static_cast<short          >(0))).name()] = "short";
    types[typeid(decltype(static_cast<unsigned int   >(0))).name()] = "unsigned int";
    types[typeid(decltype(static_cast<int            >(0))).name()] = "int";
    types[typeid(decltype(static_cast<float          >(0))).name()] = "float";
    types[typeid(decltype(static_cast<double         >(0))).name()] = "double";
    types[typeid(decltype(static_cast<bool           >(0))).name()] = "bool";

    std::cout << "Should be unsigned char : " << types[typeid(decltype(static_cast<unsigned char >(0))).name()] << std::endl;
    std::cout << "Should be unsigned short: " << types[typeid(decltype(static_cast<unsigned short>(0))).name()] << std::endl;
    std::cout << "Should be short         : " << types[typeid(decltype(static_cast<short         >(0))).name()] << std::endl;
    std::cout << "Should be unsigned int  : " << types[typeid(decltype(static_cast<unsigned int  >(0))).name()] << std::endl;
    std::cout << "Should be int           : " << types[typeid(decltype(static_cast<int           >(0))).name()] << std::endl;
    std::cout << "Should be float         : " << types[typeid(decltype(static_cast<float         >(0))).name()] << std::endl;
    std::cout << "Should be double        : " << types[typeid(decltype(static_cast<double        >(0))).name()] << std::endl;

    std::cout << "Expecting unsigned short: " << types[typeid(decltype(
        false ? static_cast<unsigned char  >(0) :
        true  ? static_cast<unsigned short >(0) :
        false ? static_cast<         short >(0) :
        false ? static_cast<unsigned int   >(0) :
        false ? static_cast<         int   >(0) :
        false ? static_cast<         float >(0) :
        false ? static_cast<         double>(0) :
                static_cast<         bool  >(0)
        )).name()] << std::endl;
}

Which resulted in the surprising output:

Should be unsigned char : unsigned char
Should be unsigned short: unsigned short
Should be short         : short
Should be unsigned int  : unsigned int
Should be int           : int
Should be float         : float
Should be double        : double
Expecting unsigned short: double

I would have expected to have seen the following output (note the last line):

Should be unsigned char : unsigned char
Should be unsigned short: unsigned short
Should be short         : short
Should be unsigned int  : unsigned int
Should be int           : int
Should be float         : float
Should be double        : double
Expecting unsigned short: unsigned short

Does anyone know why this might be happening? I am using GNU g++.

user992113
  • 163
  • 1
  • 9
  • Don't have the right compiler to check it out but following works fine for me: ` std::cout << "Expecting unsigned short: " << ( (false ? "static_cast(0)" : (true ? "static_cast(0)" : (false ? "static_cast< short >(0)" : (false ? "static_cast(0)" : (false ? "static_cast< int >(0)" : (false ? "static_cast< float >(0)" : (false ? "static_cast< double>(0)" : "static_cast< bool >(0)"))))))) ) << std::endl;` – Kashyap Oct 12 '11 at 20:32
  • Had to break it down in two comments. By works I mean it prints "(i.e. it prints: "Expecting unsigned short: static_cast(0)"):" I have gcc version 4.5.2 (GCC) – Kashyap Oct 12 '11 at 20:35

3 Answers3

8

You need to change your expectation. The type of a conditional expression depends only on the types of it's operands, not the value of its operands.

There are a number of rules that are used to determine a common type for a conditional expression from the types of the second and third operands. The values of the second and third operands, even if they are constant expressions, are not taken into account.

You should consult the standard for the details of the rules for determining the common type. If a common type can't be found the program is usually ill-formed.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • The relevant (FDIS) standardese is in §5.16/3 (conditional operator) and §5/9 (usual arithmetic conversions). For the pedantic details, see Eric Niebler's excellent article [Conditional Love: FOREACH Redux](http://www.artima.com/cppsource/foreach.html). – ildjarn Oct 12 '11 at 21:08
3

The resulting type of the ternary expression is a common type to that of the last two arguments. In C++11 there is an std::common_type trait that would get that type (which if I remember correctly is actually implemented as decltype( false ? x : y )).

What you are getting in your last expression is a type common to bool, int, short, double, etc...

K-ballo
  • 80,396
  • 20
  • 159
  • 169
1

The resulting type of the conditional expression is the type of the last two arguments. The last two arguments need to be the same type. In your example, everything is being promoted to double to satisfy this requirement. If you add some type to your expression that cannot be implicitly coerced to double (for example, void*), there will be a compiler error.

Decltype is a red-herring in this scenario. This behavior is inherited from C.

Oscar Korz
  • 2,457
  • 1
  • 18
  • 18
  • The last to operands to `?:` don't have to have identical types, there are many scenarios where they can have different types but one can be converted to the type of the other (or to a third common type). – CB Bailey Oct 12 '11 at 20:39
  • Does the promotion/coercion of the operands happen before or after the expression is evaluated? I would imagine before, and thus I think what I said is still technically correct albeit potentially misleading. – Oscar Korz Oct 12 '11 at 20:42
  • The target type is fixed at compile time because it only depends on the types of the operands. The promotion/coercion can only happen when the expression is evaluated. Only one of the second or third arguments is allowed to be evaluated so I'm not quite sure what you mean by your comment. – CB Bailey Oct 12 '11 at 20:47