1

好的,假设我想检查模板参数是否有嵌套类型/typedef XYZ。

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;
   template <class U>
   static yes f(typename U::XYZ*);
   template <class /*U*/>
   static no  f(...);
   enum {value = sizeof(f<T>(0))==sizeof(yes)};
};

工作正常,正如预期的那样。

现在考虑一下:

template <class T>
struct hasXZY
{
   typedef char                  no;
   typedef struct { char x[2]; } yes;

   static yes f(typename T::XYZ*);
   static no  f(...);
   enum {value = sizeof(f(0))==sizeof(yes)};
};

hasXYZ<int>现在导致编译时错误。好的, f 不是模板函数。但另一方面,当hasXYZ为 int via 实例化时,编译器可以很容易地从候选列表中hasXYZ<int>::value排除。f(int::XYZ*)我只是不明白为什么类模板中成员函数声明的实例化失败一定会导致整个类实例化失败。有任何想法吗?

编辑:我的问题是:为什么成员函数声明都是格式正确的?由于编译器仅在使用方法时才实例化方法,为什么需要正确声明。将上述示例 2 视为此功能的可能用例。

4

3 回答 3

4

SFINAE 仅在为函数重载决议创建候选集时使用。在您的第一个示例中,您正在调用重载的 f() 函数,由于 SFINAE,第一个函数被排除在外。

在你的第二个例子中,当实例化 hasXZY 时,它的所有成员都必须定义好,并且模板参数的替换不能失败。它适用于 int::XYZ。

成员不会因为替换失败而被排除在类之外。

于 2010-10-22T19:42:19.180 回答
3

我不是 C++ 语言律师,但我会尝试一下。

在您的第二个示例中,成员函数必须定义明确,因为它们一旦被实例化就不再是模板函数。为了让自己相信这一点,请替换“手动”:hasXZYintT

struct hasXYZ
{
    typedef int                   T;
    typedef char                  no;
    typedef struct { char x[2]; } yes;

    static yes f(T::XYZ*);
    static no  f(...);
    enum {value = sizeof(f(0))==sizeof(yes)};
};

int main()
{
    std::cout << hasXYZ::value << "\n";
}

并观察到编译失败,编译器错误与以前相同(至少在 GCC 中):

foo.cc:9: error: ‘T’ is not a class or namespace

相比之下,第一个示例在手动实例化后编译并按预期运行;成员f仍以U.

于 2010-10-22T20:57:05.423 回答
2

编辑:我的问题是:为什么成员函数声明都是格式正确的?由于编译器仅在使用方法时才实例化方法,为什么需要正确声明。将上述示例 2 视为此功能的可能用例。

当隐式实例化类模板特化时,编译器必须检查该成员的完整声明符,因为它需要了解有关声明的基本信息。这可能有助于类模板专业化的大小。

如果检查声明部分会发现它正在声明一个数据成员,sizeof那么类的值可能会产生不同的值。如果您已经声明了一个函数指针,那就是这种情况

yes (*f)(typename T::XYZ*);

C++ 语言的定义方式是,只有在解析整个声明后才能知道声明的类型。

你可以争辩说你把 static 放在那里,因此在这种情况下不需要计算它的大小。但是名称查找需要知道名称hasXZY<T>::f所指的内容以及是否声明了名称f。编译器不会实例化 的定义hasXYZ::f,但只会实例化声明的非定义部分,以获取其类型并将其名称添加到类类型中以进行名称查找。我相信在可能起作用的特定情况下支持延迟实例化来声明名称会使 C++ 编译器和 C++ 规范的实现更加复杂,而且没有可比的好处。

最后,在您尝试调用它的示例中,编译器必须实例化声明,因为它需要查找 name f,为此它需要知道该声明是函数还是其他东西。因此,如果不实例化声明,我什至在理论上都看不到您的示例可以工作的方式。请注意,在任何情况下,这些都不会实例化函数的定义。

于 2010-10-23T11:48:52.483 回答