c++ - 类模板特化推导是否应该考虑推导指导参数初始化?

标签 c++ language-lawyer c++17 template-argument-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 :

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.

其实推演指南一个可行的功能。一个函数是可行的只是意味着参数的数量匹配,约束得到满足,并且您可以为每个参数/参数对形成隐式转换序列。当我们检查 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);
}

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

我认为这是一个 gcc 错误。备案86439 .

关于c++ - 类模板特化推导是否应该考虑推导指导参数初始化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51244047/

相关文章:

c++ - 我的程序使用了无效的编译器,如何找到正确的编译器?

c++ - 当简单捕获中的标识符显示为参数的声明符 ID 时,没有编译器诊断

c++ - 保证省略和链式函数调用

c++ - 为什么std::assoc_laguerre的第二个参数是unsigned int?

c++ - 你如何复制一个对象?

c++ - Qt本地化

c++ - 为什么不能在没有可变参数的情况下在 lambda 内部转发参数?

c++ - std::find_if 映射到对象

c++ - 在 header 中定义函数是否总是使编译器将其视为内联?

c++ - 为什么 'typeid(x) == typeid(y)'评估为true,而 'x'和 'y'分别是类型T和T&的id表达式?