Let's say you have these two classes:
class A
{
public:
int a;
int b;
}
class B
{
public:
int a;
int b;
}
class C
{
public:
float a1;
float b1;
}
enum class Side { A, B };
I want a template function which takes a side
and a T
, and depending on the T
, returns a reference to "T.a
" or "T.b
" if the class has a member T::a
, or a reference to "T.a1
" or "T.b1
" if the class has a member T::a1
.
My starting point is:
template<typename T>
auto &GetBySide(const Side &side, const T &twoSided)
{
return side == Side::A?twoSided.a:twoSided.b;
}
template<typename T>
auto &GetBySide(const Side &side, const T &twoSided)
{
return side == Side::A?twoSided.a1:twoSided.b1;
}
The question is how to get the compiler to skip the first template if the member a
does not exist.
So I implemented the solution given by @Jarod42 below, but it gave errors in VS 2015 because of a bug in VS ability to differentiate between templates. Here is a work around:
template<typename T>
auto GetBySide(const Side &side, const T& twoSided)
-> decltype((twoSided.a))
{
return side == Side::A ? twoSided.a : twoSided.b;
}
// Using comma operator to trick compiler so it doesn't think that this is the same as above
template<typename T>
auto GetBySide(const Side &side, const T &twoSided)
-> decltype((0, twoSided.a1))
{
return side == Side::A ? twoSided.a1 : twoSided.b1;
}
// See comment above
template<typename T>
auto GetBySide(const Side &side, const T &twoSided)
-> decltype((0, 0, twoSided.a2))
{
return side == Side::A ? twoSided.a2 : twoSided.b2;
}
Another way would be to use the comma operator and a special struct which represented each "concept"