【发布时间】:2020-10-01 10:48:19
【问题描述】:
我试图了解类型特征传播背后的机制,如http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0602r4.html 中为 std::optional 所描述的那样。在处理复制操作(应有条件地定义为删除)与移动操作(不参与重载解决方案)之间存在细微差别。
造成这种差异的原因是什么,我将如何测试后者?示例:
#include <type_traits>
#include <optional>
struct NonMoveable {
NonMoveable() = default;
NonMoveable(NonMoveable const&) = default;
NonMoveable(NonMoveable&&) = delete;
NonMoveable& operator=(NonMoveable const&) = default;
NonMoveable& operator=(NonMoveable&&) = delete;
};
// Inner traits as expected
static_assert(!std::is_move_constructible<NonMoveable>::value);
static_assert(!std::is_move_assignable<NonMoveable>::value);
// The wrapper is moveable, via copy operations participating in
// overload resolution. How to verify that the move operations don't?
static_assert(std::is_move_constructible<std::optional<NonMoveable>>::value);
static_assert(std::is_move_assignable<std::optional<NonMoveable>>::value);
int main(int argc, char* argv[])
{
NonMoveable a1;
NonMoveable a2{std::move(a1)}; // Bad, as expected
std::optional<NonMoveable> b1;
std::optional<NonMoveable> b2{std::move(b1)}; // Good, see above. But
// useless as a test for
// P0602R4.
return 0;
}
奖金问题
GCC 做正确的事吗?我稍微修改了这个例子,让我们更接近一步:https://godbolt.org/z/br1vx1。在这里,我通过将复制操作声明为私有来使它们无法访问。带有 -std=c++20 的 GCC-10.2 现在无法通过静态断言并抱怨
error: use of deleted function 'std::optional<NonMoveable>::optional(std::optional<NonMoveable>&&)'
根据Why do C++11-deleted functions participate in overload resolution?,删除是在重载决议之后应用的,这可能表明移动构造函数参与了,尽管 P0602R4 说它不应该参与。
另一方面,https://en.cppreference.com/w/cpp/language/overload_resolution 一开始就声明
...如果这些步骤产生多个候选函数,则执行重载决策...
所以跳过了重载决议,因为移动构造函数是唯一的候选者?
【问题讨论】:
-
在程序中,不参与重载决议的函数与从未定义的函数没有区别。一个候选的重载决议仍然是重载决议。每个调用都受到重载决议的影响。为什么修改后的例子令人惊讶?您是否期望
std::optional<NonMoveable>应该是可移动构造的? -
我认为差异的原因是这个。如果用户请求复制并且复制构造函数不可用,则这是一个硬错误。我们不希望选择任何其他构造函数。所以我们删除了复制构造函数。如果用户请求移动并且移动构造函数不可用,我们希望回退到任何其他构造函数可以完成这项工作。不太清楚这个解释。
-
@n.'pronouns'm.:我对不可移动的结果并不感到惊讶,但我有点在黑暗中寻找一个指标,这是由不参与重载解决,而不是删除。 cppreference 引用似乎与始终完成重载解决方案相矛盾。是误会吗?
-
对,libstdc++ 似乎没有实现“不参与重载解析”位。不确定在移动 ctor 的情况下是否重要。
-
我不确定你能区分这两种情况。如果有可行的copy ctor,不管move是被删除还是不参与都会被使用。如果没有,则程序格式错误,同样不管。我找不到任何有区别的案例。
标签: c++ optional overload-resolution