我正在尝试理解P0091r3(已经被当前C ++草案标准N4606采用的“类模板的模板参数推导”论文)。
我相信我理解它是如何工作的,在最简单的情况下, template-name 标识一个模板:
template<class T>
struct S {
S(T);
S(const std::vector<T>&);
};
int main()
{
std::vector<int> v;
auto s = S(v);
}
S
标识主模板,因此我们创建一个由
template<class T> void Sctor(T);
template<class T> void Sctor(const std::vector<T>&);
并对虚构调用执行重载解析
Sctor(v)
确定在这种情况下我们要调用虚构的Sctor(const std::vector<T>&) [with T=int]
。这意味着我们最终会调用S<int>::S(const std::vector<int>&)
,一切都很有效。
我不明白的是,如果存在部分特化,这应该是如何工作的。
template<class T>
struct S {
S(T);
};
template<class T>
struct S<std::list<T>> {
S(const std::vector<T>&);
};
int main()
{
std::vector<int> v;
auto s = S(v);
}
我们直觉想要这是对S<std::list<int>>::S(const std::vector<int>&)
的调用。那是我们真正得到的吗?这指定在哪里?
基本上我并不直观地理解P0091r3的含义是“ template-name 指定的类模板”:这是指主模板,还是包括所有部分特化和显式完整专业化?
(我也不明白P0091r3对§7.1.6.2p2的更改是如何使用 inject-class-name 来破坏代码的,例如
template<class T>
struct iterator {
iterator& operator++(int) {
iterator result = *this; // injected-class-name or placeholder?
//...
}
};
但这完全是一个不同的问题。)
在任何现存版本的Clang或GCC中是否支持类模板推导和显式推导指南(可能在-f
标记下,如-fconcepts
)?如果是这样的话,我可以在现实生活中使用其中的一些例子,并且可能清除一半的困惑。
答案 0 :(得分:2)
这个提案有点滑过,但我认为目的是只考虑主类模板的构造函数。有证据表明,新的 [class.template.deduction] 有:
- 对于template-name指定的类模板的每个构造函数,具有以下属性的函数模板是候选:[...]
如果我们谈论的是“类”模板,那么这是主类模板,特别是因为名称查找([temp.class.spec] / 6)找不到类模板部分特化。这也是原型实现(见下文)的表现。
在论文中,类模板部分特化在“隐式演绎指南的优缺点”一节中进行了考虑,而是出于担心主类模板中的构造函数可能触发硬(非SFINAE)错误:< / p>
template<class T> struct X {
using ty = T::type;
static auto foo() { return typename T::type{} };
X(ty); #1
X(decltype(foo())); #2
X(T);
};
template<class T>
struct X<T*> {
X(...);
};
X x{(int *)0};
您对要考虑的类模板部分特化构造函数的请求是合理的,但请注意它可能导致歧义:
template<class T> struct Y { Y(T*); };
template<class T> struct Y<T*> { Y(T*); };
Y y{(int*) 0};
可能需要通过专门化类模板对隐式生成的演绎指南进行排名(作为打破平局)。
如果您想尝试原型实现,作者已经在github上发布了他们的clang分支:https://github.com/faisalv/clang/tree/clang-ctor-deduction。
论文中的讨论(“注入类名称注释”)表明注入类名优先于模板名称;添加措辞以确保这一点:
template-name 应命名一个不是 inject-class-name 的类模板。
答案 1 :(得分:1)
我想说P0091目前的措辞在这方面并不明确。它确实需要明确它是否只是主类模板,还是它包含所有特化的构造函数。
话虽如此,我相信P0091的意图是部分专业化不参与参数演绎。该功能允许编译器决定类的模板参数是什么。但是,选择部分特化的是这些模板参数实际上是什么。获得S<std::list<T>>
专业化的方法是在std::list
的模板参数列表中使用S
。
如果要使特定参数使用特定的特化,则应使用演绎指南。毕竟,这就是他们的目标。