c++ - 为什么 Member Detector fallback 必须是 int?

标签 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; };

但是检测器为所有类返回“true”,无论它们是否有 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

最佳答案

它不一定是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其他六种方法都比旧式成员检测器更好。

关于c++ - 为什么 Member Detector fallback 必须是 int?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34272205/

相关文章:

c++ - 模板特化中的多个 void_t 调用

c++ - 元编程和 SFINAE/std::enable_if:无限模板递归

c++ - C++ 中的模板类

c++ - 如何使这个转换隐含?

c++ - 可移动但不可复制的异常

c++ - 强制 SFINAE 使用不同的返回类型

c++ - sfinae: enable_if 条件

c++ - 为什么析构函数不能是模板?

c++ - 读取输入时Qt控制台应用程序问题

c++ - 如何使用 boost 图库广度优先搜索创建遍历顶点的队列?