Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Member Detector fallback have to be int?

Tags:

c++

sfinae

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

like image 817
David Doria Avatar asked Dec 14 '15 16:12

David Doria


1 Answers

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.

like image 159
Barry Avatar answered Sep 29 '22 13:09

Barry