【问题标题】:Template equivalence or template functional equivalence?模板等价还是模板功能等价?
【发布时间】:2017-12-07 00:59:18
【问题描述】:

在 C++ 标准 [temp.over.link] 中,解释了函数模板等价性的确定不应涉及编译器的“英勇努力”。

例如,C++ 标准提出了这样的建议:

// guaranteed to be the same
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);
// guaranteed to be different
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);
// ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);

这条规则是否也适用于涉及元编程的案例,如下例所示?

template<class T>
struct t_{
   using type = T;
   };
//ill-formed or different?
template<class T> T f(T);
template<class T> typename t_<T>::type f(T);

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    让我们从简单的案例开始:

    template <typename T> using id = T;
    
    template<class T> T f(T);
    template<class T> id<T> f(T);
    

    根据the relevant rule,这对我来说显然是不正确的,不需要诊断。这两个声明在功能上是等效的,但不是简单地重命名模板参数(并且没有任何依赖名称需要考虑)。


    用更复杂的情况:

    template <typename T> struct id_t { using type = T; };
    template <typename T> using id = typename id_t<T>::type;
    
    template<class T> T f(T);
    template<class T> id<T> f(T);
    

    我认为这可能不是格式错误,因为它们在功能上并不真正等效。 id_t 可能有特殊化,因此这两个声明实际上是不同的——所以它们实际上是不同的声明。

    【讨论】:

    • 哈哈,clang 和 gcc 崩溃,因为“id_t”是 typdef(需要包含 iostream)
    • 最后的要点是,实际上可以有不同的声明。但是,在这种情况下,是否有可能构建这样的特化,使得结果函数调用尝试不模棱两可?
    • @AndyG 就是这样,我在您的回答的最后评论中发布的问题Tid&lt;T&gt; 不等价,因为id&lt;T&gt; 可以专门化。他真的很强!!
    • 第一个例子在我看来像是一个重新声明——尽管它实际上是另一个“别名模板有多透明?”问题(参见 CWG 1979 和其中链接的问题)。请注意,基于令牌身份的“等效”规则仅适用于表达式。
    【解决方案2】:

    我认为在这种情况下它们被认为是不同的:

    template<class T>
    struct t_{
       using type = T;
       };
    //ill-formed or different?
    template<class T> T f(T);
    template<class T> typename t_<T>::type f(T);
    

    因为 [temp.over.link] 说(强调我的

    用于判断两个 从属名称是等价的,只考虑名称本身,而不是名称查找的结果 模板的上下文

    由于Ttypename t_&lt;T&gt;::type 的命名不同,编译器将接受这些模板声明。

    现在,您实际上无法调用其中任何一个,因为生成的模板实例在功能上是等效的,从而导致歧义。这种程序格式错误,不需要诊断*(请参阅@Barry's great answer)。

    MSVC 19.00.23506clang 6.0.0gcc 8.0.0 似乎都同意我的观点(他们都没有发布诊断)。

    请注意,编译器允许声明这些模板,只要它们不被调用,而这样的东西即使没有实例化模板也不会编译:

    template<class U> typename t_<U>::type f(U){return {};}
    template<class T> typename t_<T>::type f(T){return {};}
    

    * "如果程序包含函数声明 功能等效但不等效的模板,程序格式错误,无诊断性 必填。”

    【讨论】:

    • 我问这个问题是因为这个tricky case。我见过编译器都同意,但由于“不需要诊断”,我很怀疑。但是我仍然怀疑,您引用的段落是关于等效,但这并不排除它们是功能等效
    • @Oliv:如果我的理解是正确的,我们必须区分“等效”和“功能等效”。可能在功能上等价,但不等价,这是格式错误的。
    • 所以要证明“不是病态的”,你必须证明这些声明“不等价”(你做了什么),你还必须证明它们“在功能上不等价” .你还没有完成最后的演示,这对你来说似乎很明显,但这正是我的问题:为什么 t_&lt;T&gt;::typeT 在功能上不等价,它们导致相同的类型??
    • 正是因为t_&lt;T&gt;::type 始终是T,所以它们在功能上是等效的。但是,理论上您可以专门化 t_ 来创建一个功能不等效的声明。
    • “功能等效”是两个函数模板之间的关系,而不是两个函数模板特化。两个专业化之间的歧义导致重载解析失败,但这显然是不正确的,而不是 NDR。
    猜你喜欢
    • 1970-01-01
    • 2021-12-15
    • 2011-01-10
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 2011-04-30
    相关资源
    最近更新 更多