作为此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会检查。哪个编译器是正确的?
答案 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。