【问题标题】:Can't understand the rule about explicit specialization declaration for menber of an unspecialized class template无法理解关于非特化类模板成员的显式特化声明的规则
【发布时间】:2021-01-14 03:07:15
【问题描述】:

最新c++标准中非特化类模板成员的显式特化声明规则如下:

在为类模板的成员或出现在命名空间范围内的成员模板的显式特化声明中,成员模板和它的一些封闭类模板可能保持非特化,除非声明不应显式特化一个类成员模板,如果它的封闭类模板也没有明确专门化。在这样的显式特化声明中,应提供后跟模板参数列表的关键字模板,而不是在成员的显式特化声明之前的模板。模板参数列表中模板参数的类型应与主模板定义中指定的相同。

老实说,我对这一段有很多困惑。请考虑写在本规则下面的例子。

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};
template <class Y> 
template <>
void A<Y>::B<double>::mf2() { }   // error: B<double> is specialized but  
                                  // its enclosing class template A is not

正如评论所说,成员mf2 的这个明确的专业化声明格式不正确,但是,我不明白为什么这个声明通过这条规则是不正确的。我的理由是粗体字,它表示声明不应明确特化类成员模板,如果它的封闭类模板也没有明确特化。但是,在此示例中,声明是 mf2 的显式特化,它不是类成员模板,而是类模板的成员。所以严格来说,它不符合异常的条件,为什么声明是错误的?感觉这一段不太清楚。于是深入挖掘,我发现有缺陷的报告是CWG529

上面写着:

在对类模板的成员或出现在命名空间范围内的成员模板的显式特化声明中,成员模板和它的一些封闭类模板可能保持非特化,即相应的模板前缀可以指定一个模板-parameter-list 而不是 template 和命名模板的模板 ID 使用这些模板参数作为模板参数编写。在这样的声明中,模板参数的数量、种类和类型应与主模板定义中指定的相同,模板参数在模板ID中的命名顺序应与它们出现的顺序相同在模板参数列表中。 在命名成员的限定 ID 中,非特化模板 ID 不得位于模板特化的名称之前

想了想,还是觉得提案不足以解释这些案例,例如:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template<>
template <class T> 
void A<int>::B<T>::mf2(){}

首先,mf2 不是模板特化,但是模板 ID B&lt;T&gt;mf2 之前,此声明格式不正确。它仍然无法解释这个例子:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};

template<>
template <class T> 
template <class U> 
void A<int>::B<T>::mf1(U){}

mf1 是模板名称,但不是模板 ID(即模板特化)

所以在考虑了这些格式错误的例子之后,恕我直言,这个修改过的句子是这条规则的意图吗?

在为类模板的成员或出现在命名空间范围内的成员模板的显式特化声明中,成员模板和它的一些封闭类模板可能保持未特化。在这样的显式特化声明中,应提供后跟模板参数列表的关键字模板,而不是在成员的显式特化声明之前的模板。模板参数列表中模板参数的类型应与主模板定义中指定的相同。 在此声明中,nested-name-specifier 中未特化的每个 template-id 都应显式特化

template <class T1> class A {
  template<class T2> class B {
    template<class T3> 
    void mf1(T3);
    void mf2();
  };
};

template<>   // explicit specialization for `B`
template<class T>
class A<int>::B{
   template<class U>
   void mf1(U);
};

template<>
template<class T>
template <class U> 
void A<int>::B<T>::mf1(U){}

这个例子的格式不正确,虽然B&lt;T&gt; 是非特化的,但它已经被显式特化了。这是一个很好的解释吗?或者,如果我看错了原始规则,请解释如何理解它。

【问题讨论】:

    标签: c++ templates language-lawyer


    【解决方案1】:

    规则[temp.expl.spec]/p16 递归应用,它简单地说除非通向它的整个链也被特化,否则没有任何东西可以被特化。

    我们甚至没有找到mf2,因为不可能针对所有可能的A 专门化B

    template <class T1> class A {
      template<class T2> class B {
        template<class T3> void mf1(T3);
        void mf2();
      };
    };
    
    template <class Y> 
    template <>
    void A<Y>::B<double>::mf2() { }  // Not OK - can't specialize B for every possible A
    
    template <> 
    template <>
    void A<int>::B<double>::mf2() { }  // OK - full specialization
    
    template <class T1> class A {
      template<class T2> class B {
        template<class T3> void mf1(T3);
        void mf2();
      };
    };
    
    template <>
    template <class T> 
    void A<int>::B<T>::mf2(){}
    

    同样的故事:不能针对所有可能的 A&lt;int&gt;::B 专门化 mf2

    最后一个带有专用 A&lt;int&gt;::B 的示例有点令人困惑,但同样的规则仍然适用:

    template <class T1> class A {
    template <class T2> class B {
        template<class T3> 
        void mf1(T3);
        void mf2();
    };
    };
    
    template <>
    template <class T>
    class A<int>::B {   // explicit specialization for `B`
        template<class U>
        void mf1(U);
    };
    
    template <>
    template <class T>
    template <class U> 
    void A<int>::B<T>::mf1(U){} // OK (not a specialization, but an out-of-line definition)
    
    template <>
    template <class T>
    template <> 
    void A<int>::B<T>::mf1(double){} // Not OK again, the entire chain must be specialized
    
    template <>
    template <>
    template <> 
    void A<int>::B<double>::mf1(double){} // OK - full specialization
    

    CWG 529 试图解决以下事实:[temp.expl.spec]/p16 中的措辞令人困惑,因为它在实际上没有声明嵌套类的“专业化” 时谈论嵌套类。恕我直言,CWG 529 没有承认在每个嵌套级别都有一个专业化,只是它是假设的。给定第一个例子:

    template <class T1> class A {
      template<class T2> class B {
        template<class T3> void mf1(T3);
        void mf2();
      };
    };
    
    template <class Y> 
    template <>
    void A<Y>::B<double>::mf2() { }
    

    没有template &lt;class Y&gt; template &lt;&gt; class A&lt;Y&gt;::B {};。但如果有的话,它就不会编译(同样,不能针对每个可能的A 专门化B)。

    【讨论】:

    • 为了解决这个问题,“显式特化”一词是否表示实体存在显式特化,“未特化”一词表示其模板参数列表包含的模板ID一些模板参数?如果是,则考虑此示例 template &lt;&gt; template &lt;class T&gt; template &lt;&gt; void A&lt;int&gt;::B&lt;T&gt;::mf1(double){} ,其中 mf1 是类成员模板,声明用于显式特化 mf1,其封闭的 B&lt;T&gt; 已显式特化,如您在评论中所写(// B 的显式特化).
    • 为什么这个声明违反了temp#expl.spec-16
    • 回到这个例子,template &lt;class Y&gt; template &lt;&gt; void A&lt;Y&gt;::B&lt;double&gt;::mf2() { },这个声明不是用来显式特化mf2吗?其中mf2 不是模板,为什么这句话“除了声明不应显式特化类成员模板,如果它的封闭类模板也没有显式特化。”可以应用吗?虽然 mf2 的封闭类,即 A&lt;Y&gt; 没有明确专门化,但是 mf2 不是 模板
    • 换句话说,如果可以应用该语句,则声明必须显式特化一个模板。对吗?
    • 1) 参见“如果它的封闭类模板也没有显式特化,则声明不应显式特化类成员模板*”。 mf1(double) 是显式特化,B&lt;T&gt; 是其封闭模板,不是显式特化。因此违规。
    猜你喜欢
    • 2011-01-30
    • 2020-11-17
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-04
    • 2011-07-27
    相关资源
    最近更新 更多