当模板具有默认参数时,省略尖括号
Omit angle brackets when template has default parameters
>假设我们有一个带有默认模板参数的类模板:
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
),专门用于所有默认模板)充其量是边缘的,不足以成为显着改变语言几个核心方面的动力。至少这是我的预测。
- 使函数参数默认为周围范围
- 如何将值传递给其参数(默认参数)
- 当“std::make_index_sequence”和“std::index_sequence”用于模板参数默认类型时
- C ,构造器中允许的参考参数默认值
- 哪些规则控制参数默认赋值?
- 错误:上一个规范后给出的参数默认参数
- 是否可以设计一个包含模板参数默认值的类
- 构造函数中参数C++默认值
- 如何设置依赖于其他参数的参数默认值
- C++模板参数默认函数实现
- QInput对话框参数默认值
- g++4.8.2上列表方法参数默认初始化时出错
- 在函数定义中指定参数默认值会导致错误 C2143:语法错误:'='之前缺少')'
- 函数模板:将第一个模板参数默认为第二个
- 模板模板参数默认可以引用其他模板类型的参数
- 参数默认为先例参数的值
- 如何修改一个类,使它只有一个成员函数,所有参数默认
- 内置类型的模板参数默认值
- 是否可以使用构造函数或对象作为其他类方法的参数默认值?
- 模板参数默认为更高版本