【问题标题】:Does a template specialization extend or override the generic template?模板特化是扩展还是覆盖通用模板?
【发布时间】:2013-05-27 01:09:20
【问题描述】:
template<typename T>
struct A{
    void method1(){}
 };

template<>
struct A<int>{
    void method2(){}
 };

A&lt;int&gt; 会同时拥有 method1 和 method2 吗?而A&lt;float&gt; 将只有method1

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    @jogojapan 的回答解释了该语言的作用。如果您确实想为特定专业添加新成员,这里有几个解决方法:

    template<typename T>
    struct A_Base {
        void method1() {}
    };
    
    template<typename T>
    struct A : public A_Base<T> {};
    
    template<>
    struct A<int>
      : public A_Base<int>
    {
        void method2() {}
    };
    

    现在A&lt;int&gt; 有成员method1method2,但A&lt;float&gt; 没有method2

    或者(如果您可以修改主模板)...

    #include <type_traits>
    
    template<typename T>
    struct A {
        void method1() {}
    
        template<int N=0>
        auto method2() ->
        typename std::enable_if<std::is_same<T, int>::value && N==N>::type
        {}
    };
    

    template&lt;int N&gt;N==N 部分确保 std::enable_if 具有依赖值,因此在有人实际尝试将 A&lt;T&gt;::method2 与不正确的 T 参数一起使用之前不会抱怨。


    而且由于这个问题和答案似乎仍然受到关注,稍后进行编辑以在 C++20 中添加它,您可以简单地这样做:

    #include <type_traits>
    
    template<typename T>
    struct A {
        void method1() {}
    
        void method2() requires std::is_same_v<T, int> {}
    };
    

    【讨论】:

    • @aschepler - 如果A_Base 不是 模板,第一种技术(非 C++11 技术)会起作用吗?也就是说,你能有一个A&lt;int&gt;::A_Base::method2吗?我从 MSVC 中得到一个错误,我以前从未见过这个错误 (C2936)。我不确定它是否不可能,C++03 不可能,或者它是否是 MSVC 限制。
    • @jww 是的,它应该可以工作。我不确定您为什么会收到该错误。您链接的页面建议检查不匹配的大括号。如果您仍然没有看到愚蠢的错误,您可能需要编写和测试 minimal reproducible example 并发布一个新问题。
    • 谢谢@aschepler。在研究了更多之后,我面临的问题有点不同......我想要覆盖(专业化?)非模板库中的类成员。也就是说,我需要为A&lt;int&gt;::A_Base::method1提供一个专门的机构。让我看看能不能找到关于它的讨论。
    • A_base 不应该声明一个虚拟析构函数吗?
    • @fiorentinoing 可能没有必要,因为A_base 的目的不是充当有用的接口,只是帮助声明所需的功能。所以实际上并不期望有人会使用A_base*std::unique_ptr&lt;A_base&gt; 等等。
    【解决方案2】:

    每个特化都会带来一种全新的数据类型(或全新的模板,如果特化只是部分的)。来自标准(C++11):

    (第 14.5.5/2 节)每个类模板部分特化是一个不同的模板,应为模板部分特化 (14.5.5.3) 的成员提供定义。

    还有:

    (§14.5.5.3/1) [...] 类模板部分特化的成员与主模板的成员无关。应定义以需要定义的方式使用的类模板偏特化成员;主模板成员的定义永远不会用作类模板部分特化成员的定义。 [...]

    上述内容是在部分特化的上下文中陈述的,但它也适用于显式特化(如您的情况),尽管标准并没有很清楚地说明这一点。

    还请注意,您不仅需要在特化中声明您想要的所有成员函数,还需要定义它们(这里,即使是显式特化,标准也非常清楚):

    (14.7.3/5) 显式特化类的成员不会从类模板的成员声明中隐式实例化;相反,如果需要定义类模板特化的成员,则其自身应被显式定义。在这种情况下,类模板显式特化的定义应在范围内 在定义成员的位置。显式特化类的定义与生成的特化的定义无关。也就是说,它的成员不需要与生成的特化的成员具有相同的名称、类型等。 [...]

    所以,事实上,A&lt;int&gt; 将只有 method2(),而A&lt;float&gt; 将只有 method1() 作为成员。此外,如果您还要在 A&lt;int&gt; 特化中引入 method1(),它不需要与 A&lt;float&gt;::method1() 具有相同的参数类型或返回类型。

    请参阅@aschepler 的回答,了解避免为int 案例重写模板定义的可能方法。

    【讨论】:

    • @Fellowshee 是的,你可以这样做。在这种情况下,不要声明新的template &lt;&gt; struct A&lt;int&gt; { ... };,而只需在类模板定义之外声明(并定义)您要专门化的方法:A&lt;int&gt;::method1() { ... };
    • 有趣,一个类外根本没有提到的函数的类外声明和定义被认为是合法的?
    • @Fellowshee 哦,对不起,我以为您需要 method1 的专业化来处理 int 案例。如果您真的只需要为int 案例添加一个全新的method2,那么确实需要为int 重写整个模板。根据具体情况,最好将method2 添加到主模板定义但未定义(或让它抛出异常,或放入static_asssert(false,"not implemented");(后者仅适用于C++ 11) )。这样您就可以将 method2 专门用于 int 案例,而无需重写整个模板。
    • 我正在阅读 Herb Sutter 的这篇文章,该文章建议类的部分模板专业化(与我们一直在讨论的完整专业化相反)可以解决我的问题,而无需为在特殊情况下添加功能:gotw.ca/publications/mill17.htm
    • @Fellowshee 不确定我是否理解......这篇文章主要是关于函数模板(而不是类模板)。另外,要清楚部分特化这个术语:这意味着只有一些模板参数被实例化。这并不意味着只有类定义的某些部分是专门的。
    【解决方案3】:

    专业化替换通用模板。所以A&lt;int&gt; 将只有method2(),当然A&lt;double&gt; 将只有method1()

    【讨论】:

    • 我想编译器会将它们视为完全独立的类,它们之间没有任何关系,就好像它们具有完全不同的名称(在创建的源代码中它们可能会这样做?)
    • 从模板生成的每个类型都是一个完全独立的类,与从同一模板生成的其他类型完全没有关系。这没什么区别。 A&lt;int&gt;A&lt;double&gt;A&lt;char&gt; 将是三个不相关的类。
    猜你喜欢
    • 2012-04-17
    • 2018-01-05
    • 2011-12-20
    • 2012-02-20
    • 2012-07-05
    • 2016-10-17
    • 1970-01-01
    • 1970-01-01
    • 2011-04-27
    相关资源
    最近更新 更多