应该通过类模板专业化推导来考虑推导指导参数的初始化吗?

时间:2018-07-09 11:04:01

标签: c++ language-lawyer c++17 template-deduction

作为此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和gcc分离的地方。 Clang似乎没有检查推论指南假设的构造函数是否为viable function,但是gcc会检查。哪个编译器是正确的?

1 个答案:

答案 0 :(得分: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