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 do not know what this function will return: it can be either MyClass<T1> or MyClass<T2> .

How can I make it work?

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

Usage example:

 MyClass<5, 2, 8> m1; MyClass<4, 2, 1> m2; cout << foo(m1, m2); //should print m2 (I have a method used for that) 
+5
source share
6 answers

You can define two functions, of which only one will be created for the 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

+2
source

What you are trying to achieve cannot be done as it 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 you are trying to solve, there may be different alternatives, such as defining a generic type that can be used to represent either bar1 or bar2 , or to reorganize the code, so you don't need to return everything.

That is, if something cannot be determined at compile time ... if it can be determined that you can use metaprogramming to determine the type of return you need.

You should provide a more detailed description of the real problem, after which you can get clearer ideas as to which direction to go.


You can try something in the lines:

 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))>()); } 
+4
source

I would suggest making a callback instead of a return value, remember not to ask for a principle:

 template<typename T> struct op{ void operator ()(T t){} }; template<> struct op<int>{ void operator ()(int a){} }; template<typename T> struct Func : public op<T>{ int a; }; template<typename T, typename T2> void foo( Func<T> t, Func<T2> t2){ ta = 3; t2.a = 4; if( ta > t2.a ){ t( 3 ); }else{ t2( 5 ); } } 

Maybe there is a better solution. Or you can use operator () in MyClass, just specialize in it.

+2
source

You can return a union type, which can be T1 or T2 .

+1
source

Since the return type should be fixed as compilation time, you should return something that can be either MyClass<T1> or MyClass<T2> . It can be either a general object, such as boost::any (the excess bit for this situation), or a general base, link (or pointer), which you then return to. This requires your classes to be defined as

 class MyClassBase { /* ... */ }; template<typename T> class MyClass : MyClassBase { /* ... */ }; template<typename T1, typename T2> MyClassBase& foo(MyClass<T1>&bar1,MyClass<T2>&bar2) { if (something) return bar1; else return bar2; } 

In fact, you can use RTTI so that the MyClassBase base can determine what it really is. This is actually something like boost::any works.


Of course, as David said, if something known at compile time, then your code is not a very good design, and instead you should use another option using compile time solutions (using template metaprogram methods).

+1
source

Both bar1 and bar2 are links, so they are returned if you somehow change them in your function. You can use the return value to indicate that:

 enum ReturnType { BAR1, BAR2 } ReturnType foo(MyClass<T1>& bar1, MyClass<T2>& bar2) { if (something) return BAR1; else return BAR2; } int main() { MyClass<T1> t1; MyClass<T2> t2; ReturnType ret = foo(t1, t2); if(ret == BAR1) { // do what you want in case of MyClass<T1> } else if(ret == BAR2) { // do what you want in case of MyClass<T2> } } 

Another way that is perhaps closer to what you want is to use a base class pointer:

 class Base { } template<typename T> class MyClass : public Base { // ... } Base* foo(MyClass<T1>& bar1, MyClass<T2>& bar2) { if (something) return &bar1; else return &bar2; } 

As vsoftco mentioned in the comments, and in Walters, the answer, returning the link, also works if it is your preference.

 Base& foo(MyClass<T1>& bar1, MyClass<T2>& bar2) { if (something) return bar1; else return bar2; } 
+1
source

Source: https://habr.com/ru/post/1207913/


All Articles