当模板具有默认参数时,省略尖括号

Omit angle brackets when template has default parameters

本文关键字:参数 默认      更新时间:2023-10-16

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

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<>,当前解决方案是这样的:

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

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

这被认为是标准中的错误吗?

不。

模板是一个命名构造,它基于一组参数生成另一个构造(类/函数/变量)。模板的名称不是它生成的构造的名称。模板的名称只是模板的名称;要命名模板生成的内容,必须提供模板参数。

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

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

有没有修复它的建议?

没有什么损坏需要修复。目前没有提案以这种方式添加更改。

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

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

这很可能是可能的。但具体说明会很复杂。你需要一个认真的规范医生,一个深层次了解C++语法的人,以确定它是否可能,以及究竟需要改变什么才能做到这一点。

但归根结底,它只对一小组选定的模板有用:具有所有参数默认值的模板。真正的问题是,这是否是一个足够常见的案例,值得付出努力。

我不认为自己是语言专家,但我的立场是,你的提案试图解决的问题要简单得多,就像std::(basic_)string这样做一样。

我们有

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

然后是一组针对"非专家"用户的typedef,例如std::string用于std::basic_string<char>

如果专家用户想要使用其他模板参数,他们可以自己定义一个类型别名,这与上述内容很好且一致。此外,这将模板与从中创建的类型完全分开。

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

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

  • 您的建议与 CTAD 之间存在一些重叠,但 CTAD 绝对是关于减少初始化的类型冗长。CTAD在其范围内提供了显着的舒适性,并且可以通过演绎指南进行扩展,而您的提案的语法糖仅在很小的使用利基中相关,好处要小得多。

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

  • 您的建议也有与演绎指南冲突的危险。谁应该赢?

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

  • 总的来说,你打算解决的问题(为库实现者节省using,或用户节省<>(或using),专门用于所有默认模板)充其量是边缘的,不足以成为显着改变语言几个核心方面的动力。至少这是我的预测。