【问题标题】:Unexpectedly missing implicitly declared copy/move constructors意外缺少隐式声明的复制/移动构造函数
【发布时间】:2018-03-14 20:05:49
【问题描述】:

考虑以下几点:

#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 中声明一个移动构造函数,您会隐式删除F 的复制构造函数,进而隐式删除C 的复制构造函数.我会尝试实现(或defaulting/deleteing)F 的复制构造函数、复制/移动赋值运算符和析构函数以遵循 5 的规则。
  • @DanielLangr 啊,好点子。
  • F 中的移动构造函数是隐式定义的。
  • F&lt;C()&gt; f_;处,C是一个不完整的类型,对应F中的R模板参数,它被用作std::is_invocable_r_v的模板参数。 std::is_invocable_r_v 的模板参数不能是不完整的类型。但我不确定这是否是报告问题的原因。如果是,那么你有未定义的行为。

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


【解决方案1】:

我最好的猜测是这里:

F<C()> f_;

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


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

【讨论】:

  • F::F 是从 C 的复制/移动构造函数的默认定义中实例化的,该构造函数执行成员直接初始化。
  • @yurikilochek 为什么会实例化模板化的F::F?它不会在任何地方调用。是否需要实例化构造函数重载解析?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-10
  • 2013-08-27
  • 1970-01-01
  • 2012-07-19
  • 2018-07-03
  • 2020-09-07
  • 2014-11-26
相关资源
最近更新 更多