5 c++ templates language-lawyer c++17
假设我们有一个带有默认模板参数的类模板:
template <typename T = int>
class Foo {};
Run Code Online (Sandbox Code Playgroud)
在函数内创建变量时,我们可以省略尖括号:
int main()
{
Foo a; // gets properly deduced as Foo<int>
}
Run Code Online (Sandbox Code Playgroud)
但我们不能对成员变量这样做:
struct S
{
Foo a; // Deduce Foo<int>
};
Run Code Online (Sandbox Code Playgroud)
我们不能有这样的派生类型:
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>&)>
Run Code Online (Sandbox Code Playgroud)
我们不能接受参数并返回它们:
Foo Bar(const Foo&); // Foo<int> (*)(const Foo<int>&)
Run Code Online (Sandbox Code Playgroud)
为什么?这是否被视为标准中的错误?有解决方案吗?省略尖括号有什么实际问题吗?
我的用例:
我有一个提供默认参数的类模板。模板参数是一个仅供专家使用的功能,我自己从未使用过,但对于那些想要完全灵活性的 1% 的专家来说,它是可用的。现在对于其他 99% 我想隐藏Foo实际上是一个类模板的事实,但它不起作用,因为用户在将Foo<>其声明为成员变量时必须键入,当前的解决方案是这样的:
template <typename T = int>
class BasicFoo {};
using Foo = BasicFoo<>;
Run Code Online (Sandbox Code Playgroud)
但它使实现代码变得复杂,而且一点也不优雅。
这是否被视为标准中的错误?
不。
模板是一个命名构造,它根据一组参数生成另一个构造(类/函数/变量)。模板的名称不是它生成的构造的名称。模板的名称只是模板的名称;要命名模板生成的内容,您必须提供模板参数。
Foo是模板的名称;Foo<>是由该模板及其关联的模板参数生成的类的名称。
C++ 有几个地方允许使用模板,其参数是从表达式序列推导出来的。但这些都是非常具体的地方,是为了方便而创建的。它们的存在并不是为了隐藏名称代表模板而不是生成的构造这一事实。
有解决方案吗?
没有什么东西坏了需要修复。目前还没有提案以这种方式添加更改。
省略尖括号有什么实际问题吗?
定义“实际问题”。理论上是否可以更改语言,以便如果模板的所有参数都是默认的,则可以在没有模板参数的情况下使用模板的名称来同时表示模板和模板生成的内容?
这或许是可能的。但具体说明起来会很复杂。您需要一位认真的规范医生,一位深入了解 C++ 语法的人,才能确定这是否可能,以及到底需要更改哪些内容才能做到这一点。
但归根结底,它只对一小部分选定的模板有用:所有参数都有默认值的模板。真正的问题是,这种情况是否足够常见,值得付出努力。