17

If I have a function template with typename T, where the compiler can set the type by itself, I do not have to write the type explicitly when I call the function like:

template < typename T > 
T min( T v1, T v2 ) {
   return ( v1 < v2 ) ? v1: v2;
}
int i1 = 1, i2 = 2; int i3 = min( i1, i2 ); //no explicit <type> 

But if I have a function template with two different typenames like:

template < typename TOut, typename TIn >
TOut round( TIn v ) {
   return (TOut)( v + 0.5 );
}
double d = 1.54;
int i = round<int>(d); //explicit <int>

Is it true that I always have to specify at least 1 typename? I assume the reason is because C++ can not distinguish functions between different return types.

But if I use a void function and handover a reference, again I must not explicitly specify the return typename:

template < typename TOut, typename TIn > 
void round( TOut & vret, TIn vin ) {
   vret = (TOut)(vin + 0.5);
}
   double d = 1.54;
   int i; round(i, d); //no explicit <int>

Should the conclusion be to avoid functions with return and more prefer void functions that return via a reference when writing templates? Or is there a possibility to avoid explicitly writing the return type? Something like "type inference" for templates. Is "type inference" possible in C++0x?

Jamal
  • 763
  • 7
  • 22
  • 32
OlimilOops
  • 6,747
  • 6
  • 26
  • 36
  • Casting between types makes the type inference idea unwieldy, so you cannot overload on return types and must specify it when it's a template parameter. – Francesco May 14 '10 at 11:35
  • You might want to work on your rounding algorithm. What should -1.54 come out as? And: what if you want to get a rounded **double** value? – UncleBens May 14 '10 at 15:43

3 Answers3

22

Overload resolution is done only based on function arguments; the return value is not used at all. If the return type cannot be determined based on the arguments, you will have to specify it explicitly.

I would not go down the path of "returning" a value through a reference parameter; that makes the calling code unclear. For example, I'd prefer this:

double x = round<double>(y);

over this:

double x;
round(x, y);

because in the latter case, it's easy to confuse input and output, and it's not at all clear that x is being modified.

In the particular case of round, you probably need only one or two types for TOut anyway, so you could just leave that template argument out:

template<typename TIn>
int roundToInt(TIn v) {
    return (int)(v + 0.5);
}

I find roundToInt(x) a little clearer than round<int>(x) because it's clear what the int type is used for.

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 3
    Concerning clarity: `round_to(x)`? ;) – UncleBens May 14 '10 at 15:50
  • @UncleBens, I'd say `roundToInt(x)` is more clear than `roundTo(x)` which is *much more clear* than `round(x)`. – Gabriel Staples Apr 06 '20 at 17:35
  • That being said, `roundTo(x)` certainly is a good alternative if you want to maintain the benefit of writing the templated function once, rather than writing a separate function for each output type to round to, while still getting the ability to round to a multitude of types. But, under most situations where you're only rounding to a few types anyway, I think the explicitness of `roundToInt(x)` is better than letting someone round to anything, so I'd choose `roundToInt(x)` (plus define one or two more following this pattern) unless I need to let someone round to virtually *any* type. – Gabriel Staples Apr 06 '20 at 17:35
3

the conclusion be to avoid functions with return and more prefer void functions that return via a reference when writing templates

No, why? What do you gain? Only type inference (so less code to write). But you lose the much more logical syntax of assigning a value (and consequently more code to write). So one thing gained, another lost. I don’t see the benefit in general.

It may even help to have to specify the template type explicitly: consider the case of lexical_cast. Not specifying the return template type would be confusing.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
2

Let me add to what the others have said by saying you should prefer C++ casting over C-style casting.

vret = (TOut)(vin + 0.5);

versus

vret = static_cast<TOut>(vin + 0.5);

static cast will always fail if you try to convert unrelated types. This can help with debugging.

wheaties
  • 35,646
  • 15
  • 94
  • 131
  • And you'll have compile time warnings/errors too, much better than finding out at runtime. – Matthieu M. May 14 '10 at 12:04
  • What would the types involved have to be, so that a `static_cast` would yield a different result than a C-style cast in this template? (Yes, in a utility function you might strive for good style, but I don't see that much benefit in pure mathematical code doing conversion between number types - to warrant a post, and not perhaps a comment.) – UncleBens May 14 '10 at 15:37
  • 1
    You may be over this post, but I had to chime in. If you use a `static_cast`, an error means that the object/executable can't even exist; then the converse is true, that is, if you have an object/executable, you know that the `static_cast` was valid between types. As you move further into template metaprogramming, you will need more control over types within their inheritance trees, e.g. in CRTP. – John P Jun 30 '18 at 05:30