Reputation: 735
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)
Upvotes: 6
Views: 1479
Reputation: 41301
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;
}
Upvotes: 2
Reputation: 208353
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))>());
}
Upvotes: 4
Reputation: 352
Both bar1 and bar2 are references so they are both returned if you change them in some way in your function. You could use a return value to indicate which:
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>
}
}
An other way that maybe is 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;
}
Just as mentioned by vsoftco in the comments and in Walters answer returning a reference works too if that is your preference.
Base& foo(MyClass<T1>& bar1, MyClass<T2>& bar2)
{
if (something)
return bar1;
else
return bar2;
}
Upvotes: 1
Reputation: 4184
I would suggest to make a callback instead of returning value, remember tell don't ask 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){
t.a = 3;
t2.a = 4;
if( t.a > t2.a ){
t( 3 );
}else{
t2( 5 );
}
}
Maybe there is a better solution. Or you can use the operator () in MyClass, just specialize it.
Upvotes: 2
Reputation: 45424
Since the return type must be fixed as compile time, you must return something that can be either MyClass<T1>
or MyClass<T2>
. This could either be a generic object such as boost::any
(a bit of an overkill for this situation) or a common base, a reference (or pointer) to which you then return. This requires your classes to be defined like
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;
}
You can actually use RTTI so that the base MyClassBase
can tell what it actually is. In fact, this is roughly how boost::any
works.
Of course, as David said, if something
is known at compile time, then your code is not really a good design and you should instead use a different one, using compile-time solutions (via template meta-programming techniques).
Upvotes: 1