意外地缺少隐式声明的复制/移动构造函数

时间:2018-03-14 20:05:50

标签: c++ copy-constructor c++17 move-constructor implicit-declaration

请考虑以下事项:

#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),则此代码将编译。

我相信他们被隐含声明的要求得到了满足。为什么他们不是?

1 个答案:

答案 0 :(得分:2)

我最好的猜测是:

F<C()> f_;

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

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