Class 模板参数推导不适用于别名模板

Class template argument deduction not working with alias template

考虑下面粘贴的代码。我定义了一个非常简单的 class,编译器为其生成一个隐式推导指南,因此可以在没有显式模板参数的情况下构造它。但是,模板参数推导 不适用于 从简单的别名模板构造对象,该模板仅直接转发到目标 class:

template< typename A, typename B >
struct Foo {
    Foo( A const &a, B const &b )
            : a_(a), b_(b)
    { }

    A a_;
    B b_;
};

template< typename A, typename B >
using Bar = Foo<A, B>;

auto foobar() {
    Foo r{1, 2};
    Bar s{3, 4};
    // ../src/geo/vector_test_unit.cpp: In function 'auto foobar()':
    // ../src/geo/vector_test_unit.cpp:16:6: error: missing template arguments before 's'
    //   Bar s{3, 4};
    //       ^
    return 1;
}

正如您从上面的代码注释中看到的那样,g++ 给我一个关于使用不带模板参数的别名模板的错误。我希望在这种情况下转发模板参数推导。

所以,我的问题:这是 class 模板参数推导提案的当前措辞的明确设计吗?或者这是该功能当前 g++ 实现中未完成的功能或错误?对于提案的作者或 C++ ISO 委员会来说,这更像是一个问题,但如果他们中的任何人看到这个: Would it was desired that the final wording of the feature include enabling alias templates such as this to also是否为他们生成了隐式指南?

我能理解,由于别名模板可以有任何类型的模板参数,编译器可能并不总是能够成功地推导出目标 class 模板参数,但在这种情况下,我希望编译器将能够以与直接针对目标 class.

相同的方式

几天前,我正在使用 --std=c++1z 从 head 构建的 gcc 进行构建。完整的版本信息是:gcc version 7.0.0 20161201 (experimental) (Homebrew gcc HEAD- --with-jit)

来自http://en.cppreference.com/w/cpp/language/template_argument_deduction,"Alias templates are never deduced."所以,这是设计使然。

这是我们在制定提案时考虑的功能,但最终从 C++17 功能集中删除,因为我们还没有足够好的设计。特别是,关于如何 select 和将演绎指南从别名模板转换为别名模板的演绎指南有一些微妙之处。如果别名模板不是另一个模板的简单别名,还有关于如何表现的悬而未决的问题。一些例子:

template<typename T> struct Q { Q(T); };     // #1
template<typename T> struct Q<T*> { Q(T); }; // #2
template<typename U> using QP = Q<U*>;
int *x;
Q p = x;  // deduces Q<int*> using #1, ill-formed
QP q = x; // deduces Q<int*> using #1, or
          // deduces Q<int**> using #2?

template<typename T> Q(T) -> Q<T>; // #3
QP r = x; // can we use deduction guide #3 here?

template<typename T> Q(T*) -> Q<T**>; // #4
int **y;
QP s = y; // can we use deduction guide #4 here?

template<typename T> struct A { typedef T type; struct Y {}; };
template<typename T> using X = typename A<T>::type;
template<typename T> using Y = typename A<T>::Y;
X x = 4;           // can this deduce T == int?
Y y = A<int>::Y(); // can this deduce T == int?

上述问题有不错的答案,但解决这些问题会增加复杂性,似乎最好是禁止推导 C++17 的别名模板,而不是匆忙加入有缺陷的东西。

更新 [C++20]:该主题已针对 C++20 重新讨论,我们批准了 P1814R0,它允许 class 模板别名模板的参数推导。

原来的例子现在有效了。对于上面的例子:

  • CTAD 仍然只考虑来自主模板的构造函数。所以在 QP q = x; 中,不考虑 #2,而是使用 #1 的构造函数。该构造函数被隐式转换为 Q:

    的指南
    template<typename T> Q(T) -> Q<T>;
    

    然后通过从右侧推导出 Q (Q<T>) 指南的右侧,将其转换为别名模板 QP 的指南别名模板 (Q<U*>),推导出 T = U*,然后将其代回指南,从而产生等同于:

    template<typename U> Q(U*) -> Q<U*>;
    // ... which is effectively ...
    template<typename U> QP(U*) -> QP<U>;
    // ... except that explicit deduction guides are not
    // permitted for alias templates
    

    然后使用该指南推导出 q 的类型,从而推导出 U = int,因此 q 的类型是 Q<int*> , 所以 q 的初始化格式错误。

  • r 的初始化确实考虑了推导指南 #3,它被转换为 QP 的指南,如上所述

  • s 的初始化确实考虑了演绎指南#4;从 Q<U*> 推导 Q<T**> 没有任何推论,所以我们保留推导指南

    template<typename T> Q(T*) -> Q<T**>;
    

    照原样,但添加一个约束条件,即推导结果必须符合QP的形式。然后我们推导出 T = int,将其代入计算结果类型 Q<int**>,并检查我们是否可以从 Q<int**> 推导出 QP<U>,我们能够。所以 s 的类型推导为 Q<int**>.

  • 别名模板的 CTAD 仅在别名模板的右侧是 simple-template-id(形式为 maybe::stuff::templatename<args>).所以 XY 都不可推导。