代码之家  ›  专栏  ›  技术社区  ›  Oliv

类模板专门化演绎是否应该考虑演绎引导参数初始化?

  •  10
  • Oliv  · 技术社区  · 6 年前

    作为后续行动 question ,我测试了clang和gcc的行为。看来这两个编译器对c++标准有不同的解释。

    在下面的示例中,如果需要根据演绎指南假设构造函数参数复制不可复制的参数,gcc将拒绝编译。Clang不执行此检查:

    #include <cstddef>
    
    struct not_copyable{
        not_copyable()=default;
        not_copyable(const not_copyable&)=delete;
    };
    struct movable{
        movable()=default;
        movable(movable&&);
    };
    
    template <typename T, size_t N>
    struct A
     { template <typename ... Ts> A (Ts const & ...) {} };
    
    template <typename T, size_t N>
    struct B
     { template <typename ... Ts> B (const Ts & ...) {} };
    
    template <typename T, typename ... Ts>
    A(T const &, Ts const & ...) -> A<T, 1U + sizeof...(Ts)>;
    
    template <typename T, typename ... Ts>
    B(T, Ts ...) -> B<T, 1 + sizeof...(Ts)>;
    
    
    int main()
     {
       not_copyable nc;
       movable m;
    
       auto a0 = A{nc};    // gcc & clang -> compile
       auto a1 = A{m};     // gcc & clang -> compile
       auto b0 = B{nc};    // clang ->compile;  gcc -> error
       auto b1 = B{m};     // clang ->compile;  gcc -> error
     }
    

    在思考中,正确的行为是在C++标准的这一段中定义的。 [over.match.class.deduct]/2 以下内容:

    初始化和重载解析如中所述执行 [dcl.init]和[over.match.ctor],[over.match.copy]或 [over.match.list](根据初始化类型而定 对于假设类类型的对象,其中 所选函数和函数模板被视为 类类型的构造函数 为了形成过载 设置 ,[…]

    我强调了“ 为了形成过载集 “因为我认为这是克朗和海合会分歧的地方。Clang似乎没有检查演绎指南假设构造函数是否是 viable function ,但海合会有。哪个编译器是对的?

    1 回复  |  直到 6 年前
        1
  •  6
  •   xskxzr    6 年前

    clang似乎不检查演绎指南假设构造函数是否是一个可行的函数,但gcc检查了。

    实际上,演绎指南 一个可行的功能。一个可行的函数只意味着参数的数量匹配,约束满足,并且可以为每个参数/参数对形成隐式转换序列。当我们检查是否存在ICS时, [over.best.ics]/2 以下内容:

    其他属性,如生存期、存储类、对齐方式、参数的可访问性、参数是否为位字段, 以及是否删除函数 ,将被忽略。

    删除一个函数不会使它不可行,这一点非常重要,因为它仍然可以成为最可行的候选函数。这意味着 not_copyable 的复制构造函数被删除应该只在我们实际调用它时生效。

    例如,gcc和clang都拒绝这个程序。 #1 是一个 可行的 候选,而且它是最可行的候选,尽管删除了复制构造函数:

    struct NC {
        NC() = default;
        NC(NC const&) = delete;
        NC& operator=(NC const&) = delete;
    };       
    
    void foo(NC );                            // #1
    template <typename T> void foo(T const&); // #2
    
    int main() {
        NC nc;
        foo(nc);
    }
    

    但我们从来没有 调用 我们用来推导的综合函数和函数模板。我们只是执行重载解析并选择最佳候选——我们只使用它来选择类类型,然后重新开始。在任何时候我们都不应该真的需要复制。

    我认为这是一个gcc错误。提出 86439 .