假设我们有一个带有默认模板参数的类模板:
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>&)
为什么?这是标准中的错误吗?有解决方案吗?省略尖括号是否有实际问题?
我的用例:
我有一个提供默认参数的类模板。 template参数是仅供专家使用的功能,我本人从未使用过,但对于希望获得总体灵活性的1%的专家来说,它是可用的。现在,对于其他99%的人,我想掩盖一个事实,Foo
实际上是一个类模板,但是它不起作用,因为用户在将其声明为成员变量时必须键入Foo<>
,当前的解决方案是:
template <typename T = int>
class BasicFoo {};
using Foo = BasicFoo<>;
但是它使实现代码复杂化,而且一点也不优雅。
答案 0 :(得分:3)
这被认为是标准中的错误吗?
否。
模板是一个命名结构,它根据一组参数生成另一个结构(类/函数/变量)。模板的名称是 not 其生成的构造的名称。模板的名称就是模板的名称;要命名模板生成的内容,必须提供模板参数。
Foo
是模板的名称; Foo<>
是由该模板及其关联的模板参数生成的类的名称。
在某些地方,C ++允许使用模板,以便从表达式序列中推导出其参数。但是这些都是非常特殊的地方,为方便起见而创建。出于隐藏的目的而存在,它们不存在,即名称代表模板而不是生成的构造。
是否有解决方案?
没有什么要修复的。目前还没有提议以这种方式添加更改。
省略尖括号是否有实际问题?
定义“实际问题”。从理论上讲,是否可以更改语言,以便如果默认所有模板参数,则可以在没有模板参数的情况下使用模板名称来同时表示模板和 ?
有可能 。但要指定复杂的。您将需要一位认真的规范医生,他需要对C ++语法有深入的了解,才能确定是否可能,以及需要进行哪些确切的更改。
但最终,它仅对一小部分选择的模板有用:这些模板的所有参数均具有默认值。真正的问题是,这种情况是否足够普遍,值得付出努力。
答案 1 :(得分:2)
我不认为自己是语言专家,但我的立场是,您的提案要解决的问题可以用与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<>;
来命名,存在以下问题:>
它使语言的语法和规范变得复杂。您需要触摸问题中提到的上下文的 all 的允许语法,并添加使用模板名称的功能,该模板名称应使用类型名称,但如果需要,则使用 相关模板具有所有模板参数的默认值。而且,如果不更改所有这些内容,则会引入奇怪的不一致行为。
您的建议和CTAD之间有些重叠,但是CTAD明确地决定减少类型冗长进行初始化。 CTAD在其范围内提供了极大的舒适性,并且可以通过推论指南进行扩展,而您建议的句法糖仅在很小的使用领域中具有相关性,而带来的好处要小得多。
存在偶然使用错误的模板参数的危险(您是指默认模板参数还是只是忘记指定所需的参数?)。即使这不是您的用例中的问题,该标准也必须考虑该潜在问题。
您的建议也有与推论指南冲突的危险。 Who should win?
使用现有的语言工具即可轻松且方便地解决您的问题(请参见上文)。我不同意这种“复杂的”实现代码(实际上,仅通过单个typedef
/ using
来增加复杂性,(重新)命名模板绝对是微不足道的工作)或者它不雅致。
总体而言,您要解决的问题(为库实现者节省using
,或为用户节省<>
(或using
,仅用于所有默认模板)是充其量没有足够的动力,并且不足以显着改变语言的几个核心方面。至少这是我的预测。