【问题标题】:Omit angle brackets when template has default parameters模板具有默认参数时省略尖括号
【发布时间】:2019-03-06 16:54:00
【问题描述】:

假设我们有一个带有默认模板参数的类模板:

template <typename T = int>
class Foo {};

在函数内部创建变量时,我们可以省略尖括号:

int main()
{
    Foo a; // gets properly deduced as Foo<int>
}

但我们不能对成员变量这样做:

struct S
{
    Foo a; // Deduce Foo<int>
};

我们不能有这样的派生类型:

Foo* ptr; // Foo<int>*
Foo& ref; // Foo<int>&
int Foo::* mem_ptr; // int Foo<int>::*
std::function<Foo(const Foo&)> fn; // std::function<Foo<int>(const Foo<int>&)>

我们不能接受参数并返回它们:

Foo Bar(const Foo&); // Foo<int> (*)(const Foo<int>&)

为什么?这是否被认为是标准中的错误?有修复它的建议吗?省略尖括号有什么实际问题吗?

我的用例:

我有一个提供默认参数的类模板。模板参数是我自己从未使用过的专家专用功能,但它适用于那些想要完全灵活性的 1% 的专家。现在对于其他 99% 的人,我想隐藏 Foo 实际上是一个类模板但它不起作用,因为用户在声明它为成员变量时必须键入 Foo&lt;&gt;,当前的解决方案是:

template <typename T = int>
class BasicFoo {};

using Foo = BasicFoo<>;

但它使实现代码复杂化,一点也不优雅。

【问题讨论】:

  • @JesperJuhl:不,这是关于成员变量中的 CTAD 的问题。这是另一回事。
  • “但它使实现代码复杂化,一点也不优雅。”您认为在代码中的某个位置添加别名很复杂,但在所有位置添加语言规则并不复杂?
  • @SergeyA 像我这样的人认为这是一个错误并在 C++20 中修复了一些情况:open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html
  • @Lyberta:“像我这样的人认为这是一个错误”不,他们没有。他们认识到可以改进的语言的一部分。 “错误”是语言中损坏的一部分。有区别。

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


【解决方案1】:

这是否被视为标准中的错误?

没有。

模板是一种命名构造,它根据一组参数生成另一个构造(类/函数/变量)。模板的名称​​不是它生成的结构的名称。模板的名称只是模板的名称;要命名模板生成的东西,您必须提供模板参数。

Foo 是模板的名称; Foo&lt;&gt; 是由该模板及其关联模板参数生成的类的名称。

有几个地方 C++ 允许以这样一种方式使用模板,即它的参数是从一系列表达式中推导出来的。但这些都是非常具体的地方,是为了方便而创建的。它们的存在并不是为了隐藏名称代表模板而不是生成的构造这一事实。

有解决方案吗?

没有任何问题需要修复。并且目前没有提议以这种方式添加更改。

省略尖括号有什么实际问题吗?

定义“实际问题”。理论上是否可以更改语言,以便如果模板的所有参数都是默认的,则可以在没有模板参数的情况下使用模板的名称来同时表示模板模板生成的东西?

这是可能可能的。但要指定会复杂。你需要一个认真的规范医生,一个对 C++ 语法有深入了解的人,来确定这是否可行,以及究竟需要改变什么才能做到这一点。

但归根结底,它只会对一小部分精选模板有用:模板的所有参数都具有默认值。真正的问题是,这是否是一个足够普遍的案例,值得付出努力。

【讨论】:

    【解决方案2】:

    我不认为自己是语言专家,但我的立场是,您的提案试图解决的问题会以与 std::(basic_)string 相同的方式得到更简单的解决。

    我们有

    template<
        class CharT,
        class Traits = std::char_traits<CharT>,
        class Allocator = std::allocator<CharT>
    > class basic_string;
    

    然后是一组typedefs 用于“非专家”用户,例如std::string 用于std::basic_string&lt;char&gt;

    如果专家用户想要使用其他模板参数,他们可以自己定义一个类型别名,这很好并且与上述一致。此外,这将 template 与由它​​创建的 types 清晰地分开。

    您建议允许所有参数的默认模板仅由MyTemplate 命名,而不是要求MyTemplate&lt;&gt;,或使用using MyTemplate = MyBasicTemplate&lt;&gt;;,但存在以下问题:

    • 它使语言的语法和规范复杂化。您需要在问题中提到的上下文中触摸 all 的允许语法,添加在需要类型名称的情况下使用模板名称的能力,但 如果相关模板具有所有模板参数的默认值。如果你不改变所有这些,你就会引入奇怪的不一致行为。

    • 您的建议与 CTAD 之间存在一些重叠,但 CTAD 绝对是关于减少类型冗长用于初始化。 CTAD 在其范围内提供了极大的便利,并且可以通过演绎指南进行扩展,而您的提案的句法糖只与很小的使用领域相关,收益要小得多。

    • 存在意外使用错误模板参数的危险(您是指默认模板参数还是忘记指定所需的参数?)。即使在您的用例中这不是问题,标准也必须关注该潜在问题。

    • 您的建议也存在与演绎指南相冲突的危险。 Who should win?

    • 使用现有的语言工具可以轻松方便地解决您的问题(见上文)。我不同意这种“复杂”的实现代码(复杂度实际上只增加了一个typedef/using,(重新)命名您的模板绝对是微不足道的工作)或者它不优雅。

    • 总体而言,您打算解决的问题(为库实现者保存 using,或为用户保存 &lt;&gt;(或 using),专门用于所有默认模板)充其量只是边缘问题,不会成为显着改变语言的几个核心方面的充分动机。至少这是我的预测。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-25
      相关资源
      最近更新 更多