意外缺少隐式声明的 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()
都没有被实例化。
考虑以下因素:
#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()
都没有被实例化。