I thought I was getting the idea of this class (from here https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector):
template<typename T>
class DetectX
{
struct Fallback { int X; }; // add member name "X"
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char ArrayOfOne[1]; // typedef for an array of size one.
typedef char ArrayOfTwo[2]; // typedef for an array of size two.
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
typedef DetectX type;
enum { value = sizeof(func<Derived>(0)) == 2 };
};
but I tried to adapt it to my case in which I was looking for a member double MyTest
. So I changed this line:
struct Fallback { int X; }; // add member name "X"
to
struct Fallback { double MyTest; };
but the detector was returning "true" for all classes regardless of if they had a MyTest member or not. I changed the line to:
struct Fallback { int MyTest; };
and then it worked as expected.
Can anyone explain why the fallback has to be an int rather than the type of the member you are actually looking for?
Here is an example where I look for X as an int but Y as a double:
#include <iostream>
#include <vector>
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector
// Standard point representation
struct Point3
{
double X,Y,Z;
};
struct SomethingElse{};
template<typename T>
class DetectX
{
struct Fallback { int X; }; // add member named "X"
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char ArrayOfOne[1]; // typedef for an array of size one.
typedef char ArrayOfTwo[2]; // typedef for an array of size two.
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
typedef DetectX type;
enum { value = sizeof(func<Derived>(0)) == 2 };
};
template<typename T>
class DetectY
{
struct Fallback { double Y; }; // add member named "Y"
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char ArrayOfOne[1]; // typedef for an array of size one.
typedef char ArrayOfTwo[2]; // typedef for an array of size two.
template<typename U>
static ArrayOfOne & func(Check<double Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
typedef DetectY type;
enum { value = sizeof(func<Derived>(0)) == 2 };
};
int main()
{
std::cout << DetectX<Point3>::value << " " << DetectX<SomethingElse>::value << std::endl;
std::cout << DetectY<Point3>::value << " " << DetectY<SomethingElse>::value << std::endl;
return 0;
}
My output is:
1 0
1 1
It doesn't have to be int
. It can be of any type. You just have to refer to it correctly, by type and name, in every place:
using Arbitrary = double;
struct Fallback { Arbitrary X; }; // <== arbitrary type, specific name X
and here:
template<typename U>
static ArrayOfOne & func(Check<Arbitrary Fallback::*, &U::X> *);
// ↑↑↑↑↑↑↑↑↑↑ ↑↑↑
// this type this name
The idea is that if T
doesn't have an X
, you'll find Fallback::X
, which will match &U::X
by type (since there's only one - the one in Fallback
). But if T
does have an X
, the lookup will be ambiguous. So it doesn't matter what type Fallback::X
has - int
is just the shortest one.
Note that in C++11, this is a lot easier with something like Yakk's can_apply
:
template <class T>
using x_type = decltype(&T::X);
template <class T>
using has_x = can_apply<x_type, T>;
See also this question for a half dozen other ways that are all better than the old-style member detector.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With