为什么成员检测器回退必须是int型?

时间:2021-08-21 12:42:10

I thought I was getting the idea of this class (from here https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector):

我想我已经有了这个类的想法(从这里开始,https://en.wikibooks.org/wiki/more_c%2b%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:

但是检测器对所有类返回“true”,而不管它们是否有MyTest成员。我把台词改成:

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?

谁能解释一下为什么回退必须是int型的,而不是您正在寻找的成员的类型?

Here is an example where I look for X as an int but Y as a double:

这里有一个例子,我把X当作int,而把Y当作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 0

1 1

1

1 个解决方案

#1


4  

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:

它不一定是int型的,它可以是任何类型的。你只需要在每个地方正确地引用它,按类型和名称:

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.

它的思想是,如果T没有X,你会发现回退:X,它将按类型匹配&U: X(因为只有一个——后备的那个)。但是如果T有一个X,查找将是不明确的。所以什么类型的回退都不重要X有- int只是最短的。

Note that in C++11, this is a lot easier with something like Yakk's can_apply:

注意,在c++ 11中,使用类似Yakk的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.

还可以通过其他六种方法查看这个问题,这些方法都比旧式的成员检测器好。

#1


4  

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:

它不一定是int型的,它可以是任何类型的。你只需要在每个地方正确地引用它,按类型和名称:

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.

它的思想是,如果T没有X,你会发现回退:X,它将按类型匹配&U: X(因为只有一个——后备的那个)。但是如果T有一个X,查找将是不明确的。所以什么类型的回退都不重要X有- int只是最短的。

Note that in C++11, this is a lot easier with something like Yakk's can_apply:

注意,在c++ 11中,使用类似Yakk的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.

还可以通过其他六种方法查看这个问题,这些方法都比旧式的成员检测器好。