提问人:康桓瑋 提问时间:9/24/2023 最后编辑:Nicol Bolas康桓瑋 更新时间:9/24/2023 访问量:86
为什么 bind_front/bind_back/not_fn/bind 需要 Args...是可移动的?
Why does bind_front/bind_back/not_fn/bind require Args... to be move-constructible?
问:
我注意到产生完美转发调用包装器的 // 都要求传入的函数参数和参数参数必须是可移动构造的。std::bind_front
std::bind_back
std::not_fn
以 std
::bind_front 的标准规范为例:
template<class F, class... Args> constexpr unspecified bind_front(F&& f, Args&&... args);
[...]
任务:
is_constructible_v<FD, F> && is_move_constructible_v<FD> && (is_constructible_v<BoundArgs, Args> && ...) && (is_move_constructible_v<BoundArgs> && ...)
是。
true
其中 是类型 ,并且是表示 的包。FD
decay_t<F>
BoundArgs
decay_t<Args>...
此外,旧版具有前提条件,并且必须满足 [func.bind.bind] 中的 Cpp17MoveConstructible 要求。std::bind
FD
BoundArgs
我可以理解这部分,因为我们必须能够将参数转发到包装器内的衰减副本。但让我感到困惑的是,为什么我们需要要求这些参数是可移动构造的?is_constructible_v
我最初的猜测是,这是为了让完美的转发调用包装器也可移动构造,因为该标准要求调用包装器必须满足 [func.require] 的 Cpp17MoveConstructible 和 Cpp17Destructible 要求。
但是,这似乎是错误的,因为如果该对象具有有效的复制构造函数,则包装非可移动对象的包装器仍然可以移动构造:
struct OnlyCopyable {
OnlyCopyable(const OnlyCopyable&) = default;
OnlyCopyable(OnlyCopyable&&) = delete;
};
struct Wrapper {
OnlyCopyable copy1;
std::tuple<OnlyCopyable> copy2;
};
static_assert(!std::move_constructible<OnlyCopyable>);
static_assert( std::move_constructible<Wrapper>); // ok
事实证明,这种额外的约束使标准拒绝以下内容:
#include <functional>
struct OnlyCopyable {
OnlyCopyable() = default;
OnlyCopyable(const OnlyCopyable&) = default;
OnlyCopyable(OnlyCopyable&&) = delete;
};
struct OnlyCopyableFun {
OnlyCopyableFun() = default;
OnlyCopyableFun(const OnlyCopyableFun&) = default;
OnlyCopyableFun(OnlyCopyableFun&&) = delete;
int operator()(auto) const;
};
int main() {
OnlyCopyable arg;
auto fun1 = std::bind_front([](auto) { }, arg); // ill-formed
OnlyCopyableFun fun;
auto fun2 = std::bind_front(OnlyCopyableFun, 0); // ill-formed
}
在我看来,这不应该是因为我没有看到拒绝上述内容的好处。
那么,为什么标准要求传递到调用包装器工厂的参数类型必须是可移动构造的呢?这背后的原理是什么?
答:
“只能复制”的类型不是 C++ 标准认可的有效、合理的代码。你可以这样做,但就标准而言,这种类型在概念上是被破坏的。因此,标准库类型的许多功能可能不适用于此类类型。
从概念上讲,移动被视为副本的专用版本,在某些情况下取代复制。这使得 move 成为副本的子集。大多数标准都是这样处理的:可以复制的类型也可以移动。
同样,从技术上讲,你可以制作一个不能像这样工作的类型,但大多数标准不会对你感到满意。例如,你不能把它们放进去。std::vector<T>
评论
explicit T(const T&)
T(T&)
std::copy_constructible
中已经正式化了,而错误是标准必须说的。;-] (即,就标准而言,OP 的“可复制”概念并不是“可复制”的实际含义)std::copy_constructible<OnlyCopyable>
评论