Ternary Conditional Operator

From Wiki

Jump to: navigation, search

[edit] Unconditionally Useful Type Deduction

The ternary conditional operator ?: in C++ has some very interesting properties that have been used successfully to implement useful utilities such as BOOST_FOREACH[1] or BOOST_TYPEOF[2] as library extensions without modifying the core language. Its most interesting property is the fact that the expression

condition ? expression1 : expression2

is just that, an expression. Therefore, it must have a type. This innocent looking conditional expression can be used to infer the type of an expression without evaluating it. Here's an example program of how this can be achieved.

#include <iostream>
#include <typeinfo>
 
// a wrapper type for type T
template <typename T>
struct wrapT
{};
 
// given an expression of type T returns a wrapT<T>
template <typename T>
wrapT<T> get_type_of_expression(const T& t)
{
    return wrapT&lt;T&gt;();
}
 
// type that is convertible to wrapT<T> for any T
struct convertible_to_wrapT
{
    template <typename T>
    operator wrapT<T>() const
    {
        return wrapT<T>();
    }
};
 
#define TYPEOF_EXPRESSION(expr) \
    (true ? convertible_to_wrapT() : get_type_of_expression(expr))
 
template <typename T>
void do_something_with_type_of_expression(wrapT<T>)
{
  if (typeid(T) == typeid(double))
      std::cout << "yay";
  else
      std::cout << "nay";
}
 
int main(int argc, char* argv[])
{
    do_something_with_type_of_expression(TYPEOF_EXPRESSION(5 + 0.5));
    return 0;
}

The interesting part in this piece of code is the TYPEOF_EXPRESSION macro, which uses the ternary conditional operator to retrieve the type of the passed in expression without actually evaluating it. The trick used here is the fact that the type of the conditional expression depends on the second and third operand of the conditional operator and the fact that depending on the condition only one of the two expressions will get evaluated.

[edit] How It Works

Let's revisit

condition ? expression1 : expression2

and let T1 be the type of expression1 and T2 the type of expression2.

Somewhat simplified, the compiler has to do the following to deduce the type of the conditional expression: Let T1 and T2 be different types and at least one of them a class type. Then if T1 can be unambiguously converted to T2, T1 is the type of the conditional expression. Similarly, if T2 can be unambiguously converted to T1, T2 is the type deduced by the compiler.

In the example program above, T1 is convertible_to_wrapT and T2 is wrapT<T>. wrapT<T> cannot be converted to anything, in particular not to convertible_to_wrapT, so it cannot be the type of the conditional expression. However, convertible_to_wrapT can be unambiguously converted to wrapT<T> since it has an appropriate conversion operator. Thus, the type of

true ? convertible_to_wrapT() : get_type_of_expression(expr)

is wrapT<T>, which is just the type of expr wrapped in our wrapT wrapper. Note that because the condition in the conditional expression is simply true, get_type_of_expression(expr) actually never gets evaluated. It's dead code. Instead, convertible_to_wrapT() is evaluated, which is a noop. So we've successfully deduced the type of the expression expr at compile-time without actually evaluating it.

For a more in-depth description of the technique described in this article see Eric Niebler's excellent article [3] on The C++ Source.

[edit] References

  1. Eric Niebler, Boost.Foreach, 2004
  2. Arkadiy Vertleyb & Peder Holt, Boost.Typeof, 2004-2005
  3. Eric Niebler, Conditional Love: FOREACH Redux, 2005
Personal tools