【问题标题】:specialize type_trait after base class = undefine behavior?基类=未定义行为之后的专门type_traits?
【发布时间】:2018-01-08 07:50:56
【问题描述】:

我想让用户控制基类的“内部类型”。此代码运行良好。

版本 1 (demo)

//library layer
template<class Derived> class BT_trait{
    public: using type=int;  
};
template<class Derived> class BT{
    public: using typee=typename BT_trait<Derived>::type;
    public: typee f(){ return 1;}
};
//user code below
class C;
template<> class BT_trait<C>{
    public: using type=std::string;  //<-- user want to set "internal type"
};
class C : public BT<C> {
    public: typee f(){ return "OK";}
};
int main(){
    C bt;
    std::cout<< bt.f();
}

如果我通过添加模板参数使其更复杂一点,它将不再可编译。
(如下所示)

第 2 版 (demo)

template<class Derived> class BT_trait{
    public: using type=int;  
};
template<class Derived,class Dummy> class BT{
    public: using typee=typename BT_trait<Derived>::type;
    public: typee f(){ return 1;}
};
//user code below
template<class T> class C;
template<class T> class BT_trait<C<T>>{
    public: using type=std::string;  
};
template<class T> class C : public BT<C<T>,T> {
    // public: typename BT<C<T>, T>::typee f(){ return "OK";} //Version #2b 
    public: typee f(){ return "OK";}  //Version #2a
    //^ error: 'typee' does not name a type; did you mean 'wctype'?
};
int main(){
    C<int> bt;
    std::cout<< bt.f();
}

但是如果我使用#2b(脏)而不是#2a(简洁),上面的代码就可以正常工作。
为什么?是否可以使#2a 工作?

根据Specialization of template function after point of use will break the compilation 的引用:-

Section [temp.expl.spec] 14.7.3p6 : 如果是模板,则为成员 模板或类模板的成员是显式特化的 那么该专业化应在第一次使用之前声明 那种会导致隐式实例化的专业化 放置在发生这种使用的每个翻译单元中;不 需要诊断。

我怀疑我的代码是未定义的行为。对吗?
我对模板专业化很陌生。

【问题讨论】:

  • 对我有用,我不明白你为什么需要在class BT 中使用 Dummy 参数?
  • @Yola 你在用VS吗?
  • @Angew 是的.....
  • @Yola VS 没有正确进行两阶段查找,因此即使符合标准的编译器没有,它也会解析代码。
  • 这在默认设置的 VS2017 中不起作用 (/permissive-)。

标签: c++ c++14 language-lawyer template-specialization typetraits


【解决方案1】:

不,您的代码没有显示 UB,甚至在编译时也不会显示。

问题是一旦C 成为模板,两阶段查找规则就开始在其中应用。基本上,在解析模板时会查找不依赖于模板参数的名称,此时T 的参数当然是未知的,因此编译器无法查看BT&lt;C&lt;T&gt;, T&gt; 并找到typee 已定义那里。所以它失败了。

您必须将typee 标记为从属名称(因为它确实依赖于T)。你有几种方法:

  • 引用它时限定它:

    public: typename C::typee f(){ return "OK";}
    

    [Live example]

  • 把它带入你的班级范围(VTT's answer 也建议):

    using typename BT<C<T>, T>::typee;
    public: typee f(){ return "OK";}
    

【讨论】:

    【解决方案2】:

    注意typee 类型取决于模板参数。作为一个选项,您可以通过添加 using 指令使 typee 可识别:

    using typename BT<C<T>, T>::typee;
    public: typee f(){ return "OK";}
    

    online compiler

    【讨论】:

    • 谢谢,但为什么需要using 行?它是脏的。我相信(对于编译器)我对基类 BT 已经足够清楚了。
    • @cppBeginner 不,它一点也不脏;这是正常的模板编程。请参阅我的答案以获得解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 2014-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    相关资源
    最近更新 更多