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

时间:2019-03-06 16:54:00

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

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

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<>;

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

2 个答案:

答案 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,仅用于所有默认模板)是充其量没有足够的动力,并且不足以显着改变语言的几个核心方面。至少这是我的预测。