关于C ++中类模板参数推导的问题17

时间:2016-09-07 07:39:51

标签: c++ templates language-lawyer template-meta-programming c++17

我正在尝试理解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)?如果是这样的话,我可以在现实生活中使用其中的一些例子,并且可能清除一半的困惑。

2 个答案:

答案 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

如果要使特定参数使用特定的特化,则应使用演绎指南。毕竟,这就是他们的目标。