2

today in Theory of Programming Language class we have seen this behaviour in Java:

public class Es {
   ...
   <Y> Y choose(Y y1, Y y2){
         Y returnVal;
         if("some test"){ returnVal = y1;} else{ returnVal = y2;}
         return returnVal;
   }
}

In Main:

Es c = new Es();
Integer i = 3;
Float f = (float) 4.5;
Number n = c.choose(i, f);

Where the "incredible" is that the method must choose between Integer and Float for the parametric type Y and opts for the nearest super-type, which is Number.

I would like to reproduce this in C++ but I am stuck...

Peter Lawrey
  • 498,481
  • 72
  • 700
  • 1,075
Francesco Pegoraro
  • 708
  • 10
  • 27
  • 2
    Those are called [Generic Methods](https://docs.oracle.com/javase/tutorial/java/generics/methods.html). I believe you want to convert this in a c++ template but I didn't used those in a long time. But this could help you to do some research – AxelH Apr 20 '17 at 09:20

2 Answers2

4

Templates do not try adjusting types when they don't match. That's why a simple implementation like the following:

template <class Y>
Y choose(Y y1, Y y2) {
    // ...
}

Fails with the following error:

main.cpp:8:5: fatal error: no matching function for call to 'choose'
    choose(1, 2.5f);
    ^~~~~~
main.cpp:3:3: note: candidate template ignored:
                    deduced conflicting types for parameter 'Y' ('int' vs. 'float')

What you want to do is let the function template take in both types, then resolve the common type:

template <class Y1, class Y2>
auto choose(Y1 y1, Y2 y2) {
    using Y = std::common_type_t<Y1, Y2>;

    Y returnVal;

    if("some test") {
        returnVal = y1;
    } else {
        returnVal = y2;
    }

    return returnVal;
}

A good idea is to make the function SFINAE-friendly, by also lifting the type deduction into its signature:

template <class Y1, class Y2>
std::common_type_t<Y1, Y2> choose(Y1 y1, Y2 y2) {
    // Same as before
}
Quentin
  • 58,778
  • 7
  • 120
  • 175
-1

Use a Template Function. The C++ code which does the same thing as your Java code is actually remarkably similar to the Java code itself:

template <typename Y>
Y choose(Y y1, Y y2) {
    Y returnVal;
    if("some test"){ returnVal = y1;} else{ returnVal = y2;}
    return returnVal;
}

You can simply call it as you would in Java—the compiler will infer the correct type:

choose(1,2); //With two ints
choose("hi","bye"); //With two char*s.

NOTE: Semantically, C++ templates are quite different from Java generics. Java generics are implemented using type erasure—the JRE has no idea of the type parameters at runtime, while C++ templates actually create a separate function or class each time the template is used for a different type. See this answer.

Edit: I misunderstood your question. Unfourtunately, I don't believe C++ has the behavior you want; you have to explicitly specify that both are the supertype.

Community
  • 1
  • 1
ostrichofevil
  • 689
  • 6
  • 18
  • "the method must choose between Integer and Float for the parametric type Y and opts for the nearest super-type" -- this is not done here. – Quentin Apr 20 '17 at 09:36
  • @Quentin I don't think there is an equivalent number super-type in C++. – ostrichofevil Apr 20 '17 at 09:37
  • @ostrichofevil No, but you can make a template whos default impl causes a compilation error if used, and then specialisations – UKMonkey Apr 20 '17 at 09:46
  • @UKMonkey you'd want overloads for that, not specializations. – Quentin Apr 20 '17 at 09:49
  • Forget about "super-types" in C++, it is not tied to such things like Java. The CPU can perform operations on identical types only. If you don't do explicit casts then the compiler will do implicit ones. What you really want is that this "common" type be able to hold the result of the operations without loss of precision (without narrowing). There is no need for inheritance relationship (or any higher-level abstraction) between the types. The only what you need is a non-narrowing common-type. Quentin's answer shows such a solution in C++. – plasmacel Apr 20 '17 at 10:12
  • @Quentin no no, I mean specialisations. Something like http://stackoverflow.com/questions/26220144/disable-default-template-and-only-use-specialization-through-sfinae – UKMonkey Apr 20 '17 at 10:20
  • @UKMonkey the issue is, choosing a specialization is only done after template argument deduction has succeeded. So that would only work if your base template has two type parameters, and you specialized every possible combination of these. Or did I miss your point? – Quentin Apr 20 '17 at 11:47
  • @Quentin No, I don't disagree that 2 template parameters and lots of specialisations would be required; but ultimately my point is many ways to skin a cat :) – UKMonkey Apr 20 '17 at 12:53