【问题标题】:Equivalence between function templates and abbreviated function templates功能模板和缩写功能模板之间的等价关系
【发布时间】:2020-08-21 14:54:18
【问题描述】:

以下所有标准引用均指current ISO Standard Working Draft,生成于 2020-06-22。


[dcl.fct]/18 声明[摘录,强调我的]:

缩写函数模板是一种具有一个或多个通用参数类型占位符 ([dcl.spec.auto]) 的函数声明。 缩写函数模板等价于函数模板 ([temp.fct]),其template-parameter-list 包含一种发明类型template-parameter 对于函数声明的每个泛型参数类型占位符,按出现顺序。 [...]

使得以下函数声明是等价的

template <typename T>
void f(T);

void f(auto);  // re-declaration

然而,我们可能会注意到,[dcl.fct]/18 的例子表明

[...]

这些声明与以下声明功能等效但不等效)。

[...]

这可能(我不确定如何解释)与前面段落中的等价陈述相冲突。

现在,GCC 10.1.0 和 Clang 10.0.0(以及 GCC:HEAD 和 Clang:HEAD)在这里都有一些混合行为。如果我们声明一个函数模板,然后使用混合的经典函数模板语法和缩写函数模板语法来定义它(/重新声明它),Clang 接受大多数情况(定义先前声明的函数)而 GCC 拒绝所有情况(参见) 重新声明为单独声明的函数,随后在重载解析中出现歧义失败):

// A1: Clang OK, GCC error
template <typename T>
void a(T);

void a(auto) {}

// B1: Clang OK, GCC error
void b(auto);

template <typename T>
void b(T) {}

// C1: Clang OK, GCC error
template <typename T, typename U>
void c(T, U);

void c(auto, auto) {}

// D1: Clang OK, GCC error
template <typename T, typename U>
void d(T, U);

template <typename T>
void d(T, auto) {}

// E1: Clang error, GCC error
template <typename T>
void e(T, auto);

template <typename T>
void e(auto, T) {}

int main() {
    a(0);      // Clang OK, GCC error.
    b(0);      // Clang OK, GCC error.
    c(0, '0'); // Clang OK, GCC error.
    d(0, '0'); // Clang OK, GCC error.
    e(0, '0'); // Clang error, GCC error.
}

奇怪的是,如果我们将函数模板设为类成员函数模板,GCC 和 Clang 都接受 A1D1 的情况,但都拒绝最后的情况 E1以上:

// A2: OK
struct Sa {
    template <typename T>
    void a(T);
};

void Sa::a(auto) {}

// B2: OK
struct Sb {
    void b(auto);
};

template <typename T>
void Sb::b(T) {}

// C2: OK
struct Sc {
    template <typename T, typename U>
    void c(T, U);
};

void Sc::c(auto, auto) {}

// D2: OK
struct Sd {
    template <typename T, typename U>
    void d(T, U);
};

template <typename T>
void Sd::d(T, auto) {}

// E2: Error
struct Se {
   template <typename T>
   void e(T, auto);
};

template <typename T>
void Se::e(auto, T) {}

带有以下错误消息:

GCC

error: no declaration matches 'void Se::e(auto:7, T)'

note: candidate is: 
  'template<class T, class auto:6> void Se::e(T, auto:6)'

叮当

error: out-of-line definition of 'e' does not match 
any declaration in 'Se'

现在,类型模板参数的 name 不需要与函数模板的重新声明(或定义)保持一致,只需命名泛型类型占位符即可。

GCC 的错误消息特别有趣,暗示发明的类型模板参数被视为具体类型而不是泛型类型占位符。

问题:

  • 对于从 A1D1 的案例(分别为拒绝和接受),GCC 和 Clang 中哪一个是正确的? GCC 和 Clang 拒绝上述情况 E2 是否正确? (工作草案的)哪些标准段落明确支持它们?

【问题讨论】:

    标签: c++ templates language-lawyer c++20


    【解决方案1】:

    这个:

    template <typename T>
    void e(T, auto);
    

    翻译成这样:

    template<typename T, typename U>
    void e(T, U);
    

    相比之下,这个:

    template <typename T>
    void e(auto, T) {}
    

    翻译为:

    template <typename T, typename U>
    void e(U, T) {}
    

    记住abbreviated function template parameters are placed at the end of the template parameter list。因此,由于颠倒了模板参数的顺序,它们并没有声明相同的模板。第一个声明一个模板,第二个声明并定义一个不同的模板。

    您不会因此而得到编译错误,因为第二个定义也是一个声明。但是,当您使用类成员时,成员外定义不是声明。因此,它们必须具有匹配的成员内声明。他们没有;因此出现错误。


    至于其他,“功能等效(但不等效)”文本是非规范性符号。您引用的实际规范性文本明确指出这些是“等效的”,而不仅仅是“功能等效”。而且由于术语“等效”,根据[temp.over.link]/7,用于匹配声明和定义,在我看来,标准规定 A 到 D 的情况很好。

    奇怪的是这个非规范文本是introduced by the same proposal that introduced the normative text。但是,建议它从seems clear that it means "equivalent", not "functionally equivalent" 继承ConceptName auto 语法。

    因此,就规范性文本而言,一切似乎都很清楚。但非规范性矛盾的存在表明存在编辑问题或规范中的实际缺陷。


    虽然标准本身在措辞上是明确的并且在规范上是合理的,但这似乎不是标准作者的意图

    P0717 引入了“功能等效”的概念,以区别于“等效”。该提议被接受。但是,P0717 是在采用 C++20 的概念 TS 的早期引入的。在该提案中,它特别谈到了简洁的模板语法,并且 EWG 明确投票赞成采用“功能等效”的措辞,而不是 Concepts TS 的“等效”措辞。

    也就是说,P0717 明确表示委员会打算要求用户使用一致的语法。

    但是,Concepts TS 中的简洁模板语法已从 C++20 中删除(或者更确切地说,从未真正添加)。这意味着任何“功能等效”的措辞都从未出现过,因为该功能从未出现过。

    然后发生了 P1141,它添加了缩写模板语法,涵盖了 Concepts TS 简洁模板语法的大部分基础。但是,尽管 P0717 的作者之一是 P1141 的作者,但显然有人在措辞上犯了错误,没有人注意到它。这可以解释为什么非规范性文本指出缺乏真正的对等:因为这实际上是委员会的意图。

    所以这很可能是规范文本中的错误。

    【讨论】:

    • 啊,我明白了。按照这个推理,那么 A1D1(类似地,A2D2)是否是良构的,这样 GCC 通过 D1 拒绝 A1 是错误的?
    • @dfri:我没这么说。我只说 case E 肯定不匹配 以及为什么。也就是说,它们不是任何一种形式的“等价物”。
    • 是的,您是正确的,很抱歉在您的答案之外跳出进一步的结论。我的解释是示例 A1D1 中的两个声明声明了相同的等效函数模板,因此 GCC 的当前 HEAD 有一个错误/尚未完全兼容 w.r.t。这个功能。
    • @dfri:我添加了一些关于案例 A 到 D 的讨论。
    • 这绝对意味着在功能上是等效的,但不是等效的,但是意图很清楚,规范文本可能无论如何都可以在编辑上进行修复。请参阅 wg21.link/P0717R1 中的基本原理,其中报告了 15/14/13/2/3 的 EWG 民意调查。
    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 2020-06-26
    • 1970-01-01
    • 2011-03-27
    • 2022-01-05
    • 2011-10-24
    • 1970-01-01
    相关资源
    最近更新 更多