为什么类模板的成员函数声明都应该是格式良好的?

发布于 2024-09-29 00:29:23 字数 994 浏览 7 评论 0原文

好的,假设我想检查模板参数是否有嵌套类型/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 现在会导致编译时错误。好的,f 不是模板函数。但另一方面,当通过 hasXYZ::valuehasXYZ 实例化为 int 时,编译器可以轻松排除 f(int::XYZ* ) 来自候选列表。我只是不明白为什么类模板中成员函数声明的实例化失败一定会导致整个类实例化失败。有什么想法吗?

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

OK, suppose I want to check whether the template parameter has a nested type/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)};
};

Works fine, as expected.

Now consider this:

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> now results in a compile-time error. OK, f is not a template function. But on the other hand when hasXYZis instantiated for int via hasXYZ<int>::value, the compiler could easily just exclude f(int::XYZ*) from candidate list. I just don't understand why a failure in the instantiation of a member functions declaration in a class template must make the whole class instantiation fail. Any ideas?

Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雪若未夕 2024-10-06 00:29:23

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

在第二个示例中,实例化 hasXZY 时,必须明确定义其所有成员,并且模板参数的替换不得失败。它适用于 int::XYZ。

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

SFINAE is used only when creating a candidate set for a function overload resolution. In your first example, you are calling the overloaded f() function, and the first one is excluded thanks to SFINAE.

In your second example, when instantiate hasXZY, all its members must be well defined, and the substitution of the template parameter must not fail. It does for int::XYZ.

Members will not be excluded from the class because of a substitution failure.

茶色山野 2024-10-06 00:29:23

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

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

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 为模板。

I'm not a C++ language lawyer, but I'll have a go at this.

In your second example, the member functions must be well-defined because they are no longer template functions once hasXZY is instantiated for int. To convince yourself of this, do the substitution for T "by hand":

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";
}

and observe that this fails to compile, with the same compiler error as before (in GCC, at least):

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

By contrast, the first example compiles and behaves as expected after manual instantiation; the f members are still templated on U.

橘和柠 2024-10-06 00:29:23

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

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

如果检查声明部分会发现它正在声明数据成员,则该类的 sizeof 值可能会产生不同的值。如果您要声明一个函数指针,则会出现这种情况

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

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

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

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

Edit: My question is: why should the member function declararions be all well-formed? Since the compiler instantiates the methods only upon their usage, why does it need correct declaration. Consider the above example2 as a possible use-case of this feature.

When implicitly instantiating a class template specialization, the compiler has to inspect the complete declarator of that member because it needs to know basic information about the declaration. Such can contribute to the size of the class template specialization.

If inspecting the declaration part will find out it's declaring a data-member, the sizeof value of the class will possibly yield a different value. If you would have declared a function pointer instead, this would be the case

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

The C++ language is defined in such a way that the type of a declaration is known only once the whole declaration is parsed.

You can argue that you put static there, and thus in this case this is not needed to compute its size. But it is needed for name-lookup to know what a name hasXZY<T>::f refers to and that there was declared a name f at all. The compiler will not instantiate the definition of hasXYZ::f, but it will only instantiate the non-definition part of the declaration, to gets its type and adding its name to the class type for name lookup purposes. I believe supporting delayed-instantiation for declaration of names in particular cases where it would possibly work would complicate implementation of C++ compilers and the C++ spec even more, for no comparable benefit.

And finally, in your example where you attempt to call it, the compiler has to instantiate the declaration, because it needs to lookup the name f, and for this it needs to know whether that declaration is a function or something else. So I really even theoretically can't see a way your example could work without instantiating the declaration. Note that in any case, these will not instantiate a definition of the function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文