【问题标题】:Class template argument deduction not working with alias template类模板参数推导不适用于别名模板
【发布时间】:2016-12-07 01:49:37
【问题描述】:

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

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++ 给我一个关于使用没有模板参数的别名模板的错误。我希望在这种情况下可以转发模板参数推导。

那么,我的问题:这是对类模板参数推导提案的当前措辞的明确设计吗?或者这是该功能的当前 g++ 实现中未完成的功能或错误?对于提案的作者或 C++ ISO 委员会来说,这将是一个更大的问题,但如果他们中的任何人看到这一点:是否希望该功能的最终措辞包括启用这样的别名模板也是否为他们生成了隐式指南?

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

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

【问题讨论】:

    标签: c++ c++17


    【解决方案1】:

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

    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,它允许为别名模板推导类模板参数。

    原始示例现在有效。对于上面的例子:

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

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

      然后通过从别名模板 (Q&lt;U*&gt;) 的右侧推导出 Q (Q&lt;T&gt;) 的指南的右侧,将其转换为别名模板 QP 的指南),推导出 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&lt;int*&gt;,所以q的初始化是病态的.

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

    • s的初始化确实考虑了推导#4;从Q&lt;U*&gt; 推导出Q&lt;T**&gt; 什么都不推,所以我们保留推导指南

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

      原样,但添加一个约束,即推导结果必须与QP的形式匹配。然后我们推导出T = int,将其代入以计算Q&lt;int**&gt;的结​​果类型,并检查我们是否可以从Q&lt;int**&gt;推导出QP&lt;U&gt;,这是我们可以做到的。所以s的类型推导为Q&lt;int**&gt;

    • 仅当别名模板的右侧是 simple-template-id(格式为 maybe::stuff::templatename&lt;args&gt;)时,才支持别名模板的 CTAD。所以XY 都不能推导出来。

    【讨论】:

    • 我正在一次一点地构建和编译您的示例文件,以尝试逐步理解它。我刚刚只使用#1、#2 和int *x; Q p = x; 行编译了它。正如你所指出的,它会给出一个错误,除非我删除 #2 并重新编译。来自 g++ 的错误消息是 [...] error: invalid conversion from 'int*' to 'int' ... Q p = x; ... note: initializing argument 1 of 'Q&lt;T*&gt;::Q(T) [with T = int]' template&lt; typename T &gt; struct Q&lt;T*&gt; { Q(T); };。对我来说,这看起来像是在尝试使用#2 而不是你所说的#1。我不明白什么?
    • @squidbidness 行Q p = x;p 的类型推导出为Q&lt;int*&gt;,使用#1 进行类模板参数推导。选择该类型后,它推断Q&lt;int*&gt; 应该使用Q&lt;T*&gt; 部分特化,推断T = int。最后,它尝试使用int* 类型的参数调用Q&lt;int*&gt; 构造函数,这不起作用,因为唯一的构造函数(除了默认/复制/移动ctors)采用int
    • 感谢您的澄清。让我感到困惑的部分是我的心智模型已经推断出p 的类型并选择模板专业化作为一个相同的过程。指出它们是单独的步骤有助于我更好地推理它。
    • 这方面有 C++20 论文吗?
    • @user2023370 是的,查看编辑(如果您还没有)
    【解决方案2】:

    来自http://en.cppreference.com/w/cpp/language/template_argument_deduction,“永远不会推导出别名模板。”所以,这是设计使然。

    【讨论】:

      猜你喜欢
      • 2018-08-26
      • 2019-11-29
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-09
      • 1970-01-01
      相关资源
      最近更新 更多