No, this post is not an attempt at sneaking the word “plus” into the title three times. Instead, I want to share a short C++ trick today, which I stumbled upon half a year ago when I was teaching my students the difference between some of the operators in C and C++, so I hope this is useful to some of you.

Dealing with function template code can sometimes result in errors being emitted by the compiler because template argument deduction is ambiguous. Most of the time, casts are used to battle these errors, but often there’s a more elegant solution to the problem.

The following code shows a very simple function template for returning the maximum of any two values, build as a template in order for it to work on any type:

template <typename T>
 
  T Max(T lhs, T rhs)
 
  {
 
    return (lhs >= rhs) ? lhs : rhs;
 
  }

The function is built as a template in order to be able to specialize it for e.g. float, using fast, branch-less floating-point selects (fsel) on consoles. The same can be done for other functions such as Min, Clamp, etc. – but let’s not digress.

Such a template function can either be called with or without an explicit template argument:

// explicit template argument
 
  int max = Max<int>(100, 200);
 
   
 
  // template argument omitted
 
  int max = Max(100, 200);

If the template argument is omitted when calling a template function, figuring out the correct type T is up to the compiler, which is done using a process known as template argument deduction.

But when dealing with template argument deduction, the values involved do not undergo standard conversions such as type promotions:

int i = 100;
 
  int max = Max(i, 200u);

The above code will result in the following error:

error C2782: 'T Max(T,T)' : template parameter 'T' is ambiguous
 
  could be 'unsigned int'
 
  or       'int'

Of course, in this example we could have just written 200 instead of 200u and that would have fixed the problem, but it gets more interesting when dealing with enums and class conversion operators:

enum Test
 
  {
 
    VALUE_1 = 1,
 
    VALUE_10 = 10
 
  };
 
   
 
  struct TestClass
 
  {
 
    operator int(void)
 
    {
 
      return 10;
 
    }
 
  };
 
   
 
  // ambiguous, eventhough enums can be implicitly converted to ints
 
  int max = Max(100, VALUE_10);
 
   
 
  // needs an explicit cast
 
  int max = Max(100, static_cast<int>(VALUE_10));
 
   
 
  // ambiguous, eventhough TestClass defines a conversion operator
 
  TestClass tc;
 
  int max = Max(100, tc);
 
   
 
  // again, needs an explicit cast
 
  TestClass tc;
 
  int max = Max(100, static_cast<int>(tc));

As can be seen, even though 10 and VALUE_10 can easily be treated as ints, the template argument deduction process is ambiguous, because the types the compiler sees are int and Test, hence it doesn’t know which one to choose. The same happens with instances of type TestClass, which defines a conversion operator to int – in both cases we need to add an explicit cast as a workaround.

It would be nice if we could somehow tell the C++ compiler to carry out conversion and type promotion first, before deducing template arguments. Luckily, it turns out that there is a way to do that, by way of using the unary+ operator:

// works with type promotion
 
  int max = Max(100, +VALUE_10);
 
   
 
  // works with conversion operators
 
  TestClass tc;
 
  int max = Max(100, +tc);

Notice the plus in front of VALUE_10 and tc. Using this operator, the types used in template argument deduction will first undergo implicit conversions, solving such problems without having to either assign to a temporary variable, or using an explicit cast.

 This is an older post from my blog, but I figured it might be useful to others as well.