class 模板特化推导是否应该考虑推导指导参数初始化?

Should deduction guide argument initialization considered by class template specialization deduction?

作为此 的跟进,我测试了 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:

Initialization and overload resolution are performed as described in [dcl.init] and [over.match.ctor], [over.match.copy], or [over.match.list] (as appropriate for the type of initialization performed) for an object of a hypothetical class type, where the selected functions and function templates are considered to be the constructors of that class type for the purpose of forming an overload set,[...]

我强调“是为了形成一个重载集”,因为我认为这就是clang和gcc的分歧所在。 Clang 似乎不检查推导指南假设构造函数是否为 viable function,但 gcc 会检查。哪个编译器是正确的?

Clang does not seem to check if the deduction guide hypothetical constructor is a viable function, but gcc does.

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

Other properties, such as the lifetime, storage class, alignment, accessibility of the argument, whether the argument is a bit-field, and whether a function is deleted, are ignored.

非常重要的一点是,删除一个函数不会使其变得不可行,因为重要的是它仍然可以最终成为最佳可行候选者。这意味着 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);
}

但我们实际上从未调用我们用于演绎的合成函数和函数模板。我们只是执行重载解析并选择最佳候选者——我们只用它来选择 class 类型,然后我们重新开始。我们在任何时候都不应该真正要求复制。

我认为这是一个 gcc 错误。归档 86439.