为什么Member Detector后备必须是int?

时间:2015-12-14 16:40:38

标签: c++ sfinae

我以为我正在接受这个课程的想法(从这里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 };
};

但我尝试将其改编为我正在寻找成员double MyTest的情况。所以我改变了这一行:

struct Fallback { int X; }; // add member name "X"

struct Fallback { double MyTest; };

但探测器正在返回&#34; true&#34;适用于所有课程,无论他们是否有MyTest会员。我将行改为:

struct Fallback { int MyTest; };

然后它按预期工作。

有谁可以解释为什么后备必须是一个int而不是你实际想要的成员类型?

这是一个例子,我将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;
}

我的输出是:

1 0

1 1

1 个答案:

答案 0 :(得分:4)

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

using Arbitrary = double;

struct Fallback { Arbitrary X; }; // <== arbitrary type, specific name X

在这里:

template<typename U> 
static ArrayOfOne & func(Check<Arbitrary Fallback::*, &U::X> *);
//                             ↑↑↑↑↑↑↑↑↑↑                ↑↑↑
//                             this type              this name

我的想法是,如果T没有X,您就会找到Fallback::X,它会按类型匹配&U::X(因为只有一个 - Fallback中的一个。但如果T确实有X,则查找将不明确。因此Fallback::X具有哪种类型并不重要 - int只是最短的类型。

请注意,在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>;

另请参阅this question以了解其他六种方式,这些方式都比旧式成员检测器更好。