Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unknown return type in template

I have a function

template <typename T1, typename T2>
/*return type*/ foo(MyClass<T1>& bar1, MyClass<T2>& bar2)
{
    if (something)
        return bar1;
    else
        return bar2;
}

The problem is, that I don't know what will this function return: it can be either MyClass<T1> or MyClass<T2>.

How can I get it to work?

T1 and T2 are structs of 3 ints, known at compile time. The return type depends on the smaller of those 2: for example for T1 = <5, 1, 1> T2 = <4, 4, 7> return type should be MyClass<T2>.

Example usage:

MyClass<5, 2, 8> m1;
MyClass<4, 2, 1> m2;
cout << foo(m1, m2); //should print m2 (I have a method used for that)
like image 289
Mr M. Avatar asked Nov 28 '14 21:11

Mr M.


2 Answers

What you are trying to achieve there cannot be done as is. The language does not allow you to have a function that, based on some runtime data, changes its return type. Depending on the problem that you are trying to solve, there might be different alternatives, like determining a common type that can be used to represent either bar1 or bar2 or reorganizing the code so that you don't need to return at all.

That is if something cannot be determined at compile time... if it can be determined you can have some metaprogramming in place to determine the return type to be the one you need.

You should provide a higher level description of the real problem, after which you might get better ideas as of what direction to take.


You can try something in the lines of:

template <bool value> struct Bool {};

template <typename T, typename U>
T f_impl(T t, U u, Bool<true>) { return t; }
template <typename T, typename U>
T f_impl(T t, U u, Bool<false>) { return u; }

template <int A, int B, int C, int D, int E, int F>
auto f(MyClass<A,B,C> a, MyClass<D,E,F> b)
  -> f_impl(a, b, Bool<!(A < D 
                     || (A == D && B < E) 
                     || (A == D && B == E && C < F))>())
{
   return f_impl(a, b, Bool<!(A < D 
                          || (A == D && B < E) 
                          || (A == D && B == E && C < F))>());
}
like image 76
David Rodríguez - dribeas Avatar answered Sep 28 '22 19:09

David Rodríguez - dribeas


You can define two functions from which only one will be instantiated for given types:

template <typename T1, typename T2>
struct ChooseFirst;

template <int A1, int B1, int C1, int A2, int B2, int C2>
struct ChooseFirst<MyClass<A1, B1, C1>, MyClass<A2, B2, C2>> {
    // this requires constexpr make_tuple (C++14)
    static const bool value = std::make_tuple(A1, B1, C1) < std::make_tuple(A2, B2, C2);
    // if in your implementation make_tuple is not constexpr, you can write the comparison manually:
    // static const bool value = A1 < A2 || (A1 == A2 && (B1 < B2 || (B1 == B2 && C1 < C2)));
};

template <typename T1, typename T2>
typename std::enable_if<ChooseFirst<T1, T2>::value, T1&>::type foo(T1& bar1, T2&) {
    return bar1;
}

template <typename T1, typename T2>
typename std::enable_if<!ChooseFirst<T1, T2>::value, T2&>::type foo(T1&, T2& bar2) {
    return bar2;
}

Demo

like image 33
Anton Savin Avatar answered Sep 28 '22 21:09

Anton Savin