意外缺少隐式声明的 copy/move 构造函数

Unexpectedly missing implicitly declared copy/move constructors

考虑以下因素:

#include <type_traits>

template <typename>
struct F;

template <typename R, typename... As>
struct F<R(As...)>
{
    template <typename F_, std::enable_if_t<
        std::is_invocable_r_v<R, std::decay_t<F_>, As...>>*...>
    F(F_&&) {}

    F() = default;

    template <typename... As_, std::enable_if_t<
        std::is_invocable_v<void(As&&...), As_...>>*...>
    R operator()(As_&&...)
    { return R(); }
};

struct C
{
    F<C()> f_;

    // C(C&&) = default;  // <<< 1
};

int main() {
    F<C(int)> x;
    auto y = x;
}

gcc 7.3.0 编译失败(在 std::is_invocable_r 的深处):

error: invalid use of incomplete type
    ‘struct std::__or_<std::is_void<C>, std::is_convertible<C, C> >’

和 clang 5.0.1 一样:

error: no type named 'type' in
    'std::__or_<std::is_void<C>, std::is_convertible<C, C> >'

据此我推断 C 缺少移动和复制构造函数。实际上,如果我们取消注释其移动构造函数 (1),此代码将编译。

我认为隐式声明的要求已经满足。他们为什么不呢?

我最好的猜测是:

F<C()> f_;

C 是一个不完整的类型。在 F 中,C 替换模板参数 R,然后用作 std::is_invocable_r_v 的模板参数。标准不允许使用不完整的类型作为 std::is_invocable_r_v 的模板参数,这会导致未定义的行为。未定义的行为包括编译器在编译期间的任意行为。


请注意,我并不完全确定我的答案,主要是因为模板化的 F::F 构造函数及其模板化的 operator() 都没有被实例化。